aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Hofmann <Johannes.Hofmann@gmx.de>2010-08-20 23:24:19 +0200
committerJohannes Hofmann <Johannes.Hofmann@gmx.de>2010-08-20 23:24:19 +0200
commitf5c598b518d1f906148534d015f50075d3e8242d (patch)
tree21dd70add5b366c3dd80641b77f6b18e0baa009e
parente98d02a01ffeb18ede86af025e51ae1ec011c75a (diff)
parent5f0fc0e48b8cbee7e1795935da0abff6627fd498 (diff)
merge
-rw-r--r--.hgignore46
-rw-r--r--AUTHORS41
-rw-r--r--ChangeLog243
-rw-r--r--Doxyfile600
-rw-r--r--README37
-rw-r--r--configure.in43
-rw-r--r--dillorc135
-rw-r--r--dlib/dlib.c78
-rw-r--r--dlib/dlib.h20
-rw-r--r--doc/CCCwork.txt153
-rw-r--r--doc/Cache.txt89
-rw-r--r--doc/Cookies.txt16
-rw-r--r--doc/Dillo.txt43
-rw-r--r--doc/Dpid.txt293
-rw-r--r--doc/HtmlParser.txt52
-rw-r--r--doc/IO.txt14
-rw-r--r--doc/Images.txt68
-rw-r--r--doc/Imgbuf.txt4
-rw-r--r--doc/Makefile.am3
-rw-r--r--doc/NC_design.txt115
-rw-r--r--doc/README30
-rw-r--r--doc/Selection.txt2
-rw-r--r--doc/dillo.1101
-rw-r--r--doc/dw-changes.doc8
-rw-r--r--doc/dw-example-screenshot.pngbin0 -> 3808 bytes
-rw-r--r--doc/dw-images-and-backgrounds.doc2
-rw-r--r--doc/dw-layout-views.doc66
-rw-r--r--doc/dw-layout-widgets.doc27
-rw-r--r--doc/dw-overview.doc10
-rw-r--r--doc/dw-size-of-widget.pngbin0 -> 2825 bytes
-rw-r--r--doc/dw-style-box-model.pngbin0 -> 5116 bytes
-rw-r--r--doc/dw-style-length-absolute.pngbin0 -> 756 bytes
-rw-r--r--doc/dw-style-length-percentage.pngbin0 -> 1105 bytes
-rw-r--r--doc/dw-style-length-relative.pngbin0 -> 1083 bytes
-rw-r--r--doc/dw-textblock-collapsing-spaces-1-1.pngbin0 -> 641 bytes
-rw-r--r--doc/dw-textblock-collapsing-spaces-1-2.pngbin0 -> 521 bytes
-rw-r--r--doc/dw-textblock-collapsing-spaces-2-1.pngbin0 -> 802 bytes
-rw-r--r--doc/dw-textblock-collapsing-spaces-2-2.pngbin0 -> 586 bytes
-rw-r--r--doc/dw-usage.doc8
-rw-r--r--doc/dw-viewport-with-scrollbar.pngbin0 -> 755 bytes
-rw-r--r--doc/dw-viewport-without-scrollbar.pngbin0 -> 542 bytes
-rw-r--r--doc/dw-widget-sizes.doc4
-rw-r--r--doc/fltk-problems.doc37
-rw-r--r--doc/uml-legend.doc6
-rw-r--r--doc/user_help.html298
-rw-r--r--dpi/Makefile.am7
-rw-r--r--dpi/bookmarks.c286
-rw-r--r--dpi/cookies.c1613
-rw-r--r--dpi/datauri.c191
-rw-r--r--dpi/downloads.cc108
-rw-r--r--dpi/dpiutil.c149
-rw-r--r--dpi/dpiutil.h45
-rw-r--r--dpi/file.c827
-rw-r--r--dpi/ftp.c110
-rw-r--r--dpi/hello.c81
-rw-r--r--dpi/https.c247
-rw-r--r--dpi/vsource.c239
-rw-r--r--dpid/Makefile.am24
-rw-r--r--dpid/dpi.c4
-rw-r--r--dpid/dpi.h17
-rw-r--r--dpid/dpi_service.c114
-rw-r--r--dpid/dpi_service.h20
-rw-r--r--dpid/dpi_socket_dir.c3
-rw-r--r--dpid/dpid.c547
-rw-r--r--dpid/dpid.h46
-rw-r--r--dpid/dpid_common.c32
-rw-r--r--dpid/dpid_common.h9
-rw-r--r--dpid/dpidc31
-rw-r--r--dpid/dpidc.c121
-rw-r--r--dpid/dpidrc.in6
-rw-r--r--dpid/main.c118
-rw-r--r--dpid/misc_new.c55
-rw-r--r--dpid/misc_new.h3
-rw-r--r--dpip/dpip.c366
-rw-r--r--dpip/dpip.h58
-rw-r--r--dw/alignedtextblock.cc17
-rw-r--r--dw/alignedtextblock.hh4
-rw-r--r--dw/bullet.cc7
-rw-r--r--dw/bullet.hh4
-rw-r--r--dw/events.hh6
-rw-r--r--dw/findtext.cc133
-rw-r--r--dw/findtext.hh22
-rw-r--r--dw/fltkcomplexbutton.cc43
-rw-r--r--dw/fltkcomplexbutton.hh38
-rw-r--r--dw/fltkflatview.cc5
-rw-r--r--dw/fltkflatview.hh2
-rw-r--r--dw/fltkimgbuf.cc123
-rw-r--r--dw/fltkmisc.cc3
-rw-r--r--dw/fltkplatform.cc184
-rw-r--r--dw/fltkplatform.hh45
-rw-r--r--dw/fltkpreview.cc16
-rw-r--r--dw/fltkpreview.hh1
-rw-r--r--dw/fltkui.cc439
-rw-r--r--dw/fltkui.hh84
-rw-r--r--dw/fltkviewbase.cc144
-rw-r--r--dw/fltkviewbase.hh26
-rw-r--r--dw/fltkviewport.cc131
-rw-r--r--dw/fltkviewport.hh6
-rw-r--r--dw/image.cc201
-rw-r--r--dw/image.hh34
-rw-r--r--dw/imgbuf.hh30
-rw-r--r--dw/iterator.cc47
-rw-r--r--dw/iterator.hh54
-rw-r--r--dw/layout.cc298
-rw-r--r--dw/layout.hh141
-rw-r--r--dw/listitem.cc17
-rw-r--r--dw/listitem.hh2
-rw-r--r--dw/platform.hh39
-rw-r--r--dw/ruler.cc5
-rw-r--r--dw/ruler.hh4
-rw-r--r--dw/selection.cc20
-rw-r--r--dw/selection.hh60
-rw-r--r--dw/style.cc272
-rw-r--r--dw/style.hh191
-rw-r--r--dw/table.cc296
-rw-r--r--dw/table.hh232
-rw-r--r--dw/tablecell.cc8
-rw-r--r--dw/textblock.cc1276
-rw-r--r--dw/textblock.hh155
-rw-r--r--dw/types.cc63
-rw-r--r--dw/types.hh29
-rw-r--r--dw/ui.cc65
-rw-r--r--dw/ui.hh169
-rw-r--r--dw/view.hh15
-rw-r--r--dw/widget.cc248
-rw-r--r--dw/widget.hh173
-rwxr-xr-xinstall-dpi-local10
-rw-r--r--lout/Makefile.am3
-rw-r--r--lout/container.cc117
-rw-r--r--lout/container.hh18
-rw-r--r--lout/identity.cc3
-rw-r--r--lout/identity.hh6
-rw-r--r--lout/misc.cc60
-rw-r--r--lout/misc.hh55
-rw-r--r--lout/msg.h39
-rw-r--r--lout/object.cc35
-rw-r--r--lout/object.hh6
-rw-r--r--lout/signal.cc11
-rw-r--r--lout/signal.hh8
-rw-r--r--src/IO/IO.c49
-rw-r--r--src/IO/IO.h3
-rw-r--r--src/IO/Makefile.am2
-rw-r--r--src/IO/Url.h6
-rw-r--r--src/IO/about.c130
-rw-r--r--src/IO/dpi.c497
-rw-r--r--src/IO/http.c419
-rw-r--r--src/IO/iowatch.cc6
-rw-r--r--src/IO/mime.c12
-rw-r--r--src/IO/mime.h21
-rw-r--r--src/Makefile.am51
-rw-r--r--src/auth.c539
-rw-r--r--src/auth.h19
-rw-r--r--src/bookmark.c3
-rw-r--r--src/bw.c45
-rw-r--r--src/bw.h16
-rw-r--r--src/cache.c504
-rw-r--r--src/cache.h6
-rw-r--r--src/capi.c393
-rw-r--r--src/capi.h7
-rw-r--r--src/chain.c30
-rw-r--r--src/chain.h1
-rw-r--r--src/colors.c11
-rw-r--r--src/cookies.c89
-rw-r--r--src/cookies.h3
-rw-r--r--src/css.cc617
-rw-r--r--src/css.hh495
-rw-r--r--src/cssparser.cc1476
-rw-r--r--src/cssparser.hh54
-rw-r--r--src/decode.c29
-rw-r--r--src/dgif.h19
-rw-r--r--src/dialog.cc107
-rw-r--r--src/dialog.hh7
-rw-r--r--src/dicache.c375
-rw-r--r--src/dicache.h26
-rw-r--r--src/dillo.cc319
-rw-r--r--src/dir.c75
-rw-r--r--src/dir.h20
-rw-r--r--src/djpeg.h19
-rw-r--r--src/dns.c68
-rw-r--r--src/dns.h8
-rw-r--r--src/doctree.hh72
-rw-r--r--src/dpiapi.c13
-rw-r--r--src/dpng.h19
-rw-r--r--src/findbar.cc40
-rw-r--r--src/findbar.hh23
-rw-r--r--src/form.cc977
-rw-r--r--src/form.hh16
-rw-r--r--src/gif.c108
-rw-r--r--src/history.c38
-rw-r--r--src/history.h6
-rw-r--r--src/html.cc2068
-rw-r--r--src/html.hh3
-rw-r--r--src/html_common.hh86
-rw-r--r--src/image.cc139
-rw-r--r--src/image.hh28
-rw-r--r--src/imgbuf.cc138
-rw-r--r--src/imgbuf.hh30
-rw-r--r--src/jpeg.c121
-rw-r--r--src/keys.cc363
-rw-r--r--src/keys.hh68
-rw-r--r--src/keysrc101
-rw-r--r--src/klist.c3
-rw-r--r--src/menu.cc315
-rw-r--r--src/menu.hh21
-rw-r--r--src/misc.c117
-rw-r--r--src/misc.h2
-rw-r--r--src/msg.h23
-rw-r--r--src/nav.c177
-rw-r--r--src/nav.h8
-rw-r--r--src/paths.cc101
-rw-r--r--src/paths.hh26
-rw-r--r--src/pixmaps.h199
-rw-r--r--src/plain.cc90
-rw-r--r--src/png.c227
-rw-r--r--src/prefs.c528
-rw-r--r--src/prefs.h44
-rw-r--r--src/prefsparser.cc208
-rw-r--r--src/prefsparser.hh24
-rw-r--r--src/styleengine.cc678
-rw-r--r--src/styleengine.hh84
-rw-r--r--src/table.cc251
-rw-r--r--src/table.hh4
-rw-r--r--src/timeout.cc2
-rw-r--r--src/ui.cc375
-rw-r--r--src/ui.hh37
-rw-r--r--src/uicmd.cc586
-rw-r--r--src/uicmd.hh24
-rw-r--r--src/url.c174
-rw-r--r--src/url.h17
-rw-r--r--src/utf8.cc102
-rw-r--r--src/utf8.hh33
-rw-r--r--src/web.cc45
-rw-r--r--src/web.hh5
-rw-r--r--src/xembed.cc165
-rw-r--r--src/xembed.hh23
-rw-r--r--test/Makefile.am8
-rw-r--r--test/cookies.c970
-rw-r--r--test/dw_anchors_test.cc16
-rw-r--r--test/dw_border_test.cc18
-rw-r--r--test/dw_example.cc10
-rw-r--r--test/dw_find_test.cc12
-rw-r--r--test/dw_images_scaled.cc22
-rw-r--r--test/dw_images_scaled2.cc18
-rw-r--r--test/dw_images_simple.cc25
-rw-r--r--test/dw_imgbuf_mem_test.cc15
-rw-r--r--test/dw_links.cc41
-rw-r--r--test/dw_links2.cc43
-rw-r--r--test/dw_lists.cc16
-rw-r--r--test/dw_resource_test.cc14
-rw-r--r--test/dw_table.cc10
-rw-r--r--test/dw_table_aligned.cc14
-rw-r--r--test/dw_ui_test.cc16
-rw-r--r--test/fltk_browser.cc5
-rw-r--r--test/form.cc19
-rw-r--r--test/form.hh12
-rw-r--r--test/shapes.cc5
256 files changed, 20708 insertions, 10519 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 00000000..cac1f7df
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,46 @@
+(^|/)CVS($|/)
+(^|/)\.hg($|/)
+(^|/)\.deps($|/)
+(^|/)\html($|/)
+.*\.a
+.*\.o
+config\..*
+autom4te.cache
+Makefile
+Makefile.in
+configure
+aclocal.m4
+depcomp
+install-sh
+stamp-h1
+missing
+tags
+dillo
+d_size.h
+dpi/bookmarks.dpi
+dpi/cookies.dpi
+dpi/datauri.filter.dpi
+dpi/downloads.dpi
+dpi/file.dpi
+dpi/ftp.filter.dpi
+dpi/hello.filter.dpi
+dpi/https.filter.dpi
+dpid/dpid
+test/cookies
+test/dw-anchors-test
+test/dw-border-test
+test/dw-example
+test/dw-find-test
+test/dw-images-scaled
+test/dw-images-scaled2
+test/dw-images-simple
+test/dw-imgbuf-mem-test
+test/dw-links
+test/dw-links2
+test/dw-lists
+test/dw-resource-test
+test/dw-table
+test/dw-table-aligned
+test/dw-ui-test
+test/fltk-browser
+test/shapes
diff --git a/AUTHORS b/AUTHORS
index 181e2a0e..c22dd7ce 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,50 +1,55 @@
_________________________________________________________________
-
+
Dillo project's team
_________________________________________________________________
-
+
Project maintainer:
- * Jorge Arellano Cid
-
+ * Jorge Arellano Cid
+
Core Developers:
- * Jorge Arellano Cid
- * Sebastian Geerken
+ * Jorge Arellano Cid
+ * Sebastian Geerken
* Johannes Hofmann
* Place (aka corvid)
+ * Luca Rota
Steady developers:
* Livio Baldini
* Eric Gaudet
* Jeremy Henty
* Jörgen Viksell
-
+
Contributors:
* Lars Clausen
- * Geoff Lane
- * Sammy Mannaert
- * James McCollough
-
+ * Ferdi Franceschini
+ * Matthias Franz
+ * Geoff Lane
+ * Sammy Mannaert
+ * James McCollough
+ * Justus Winter
+
Patches:
* Philip Blundell
* Francis Daly
- * Sam Dennis
+ * Sam Dennis
* Melvin Hadasht
* Madis Janson
- * Andrew McPherson
+ * Frank de Lange
+ * Andrew McPherson
* Sean 'Shaleh' Perry
* Marcos Ramírez
* Adam Sampson
* Andreas Schweitzer
- * Dominic Wong
+ * Dominic Wong
_________________________________________________________________
-
+
Web site logo:
* Eric Gaudet
- * Jarrod Henry
-
+ * Jarrod Henry
+
Gzilla author:
- * Raph Levien
+ * Raph Levien
_________________________________________________________________
diff --git a/ChangeLog b/ChangeLog
index db291a66..911a715f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,245 @@
Dillo project
=============================================================================
-dillo-2.0
+dillo-2.2.1 [not released yet]
+
++- Implemented "View source" as a dpi.
+ Patch: Jorge Arellano Cid
++- Configurable User-Agent HTTP header.
+ Patch: Alexander Voigt, corvid
++- Include Accept header in HTTP queries.
+ - Work with libpng-1.4.
+ - Handle zero-width space.
+ - Fix segfault closing window from WM.
+ - Limit total number of cookies.
+ - Use the suffix/subdomain field in cookies.txt.
+ - Follow most specific matching rule in cookiesrc.
+ - Fix segfault with form inputs and repush for stylesheets.
+ - Handle white-space: pre-wrap and pre-line.
+ - Support for the word-spacing property.
+ - Fix segfault with https and self-signed certificates.
+ Patches: corvid
++- Implement line-height.
+ Patch: Johannes Hofmann, corvid
+
+-----------------------------------------------------------------------------
+
+dillo-2.2 [Feb 11, 2010]
+
++- Added keybindings for scrolling.
+ - Help button and local help file.
+ Patches: corvid, Jorge Arellano Cid
++- Add support for multiple class names in CSS.
+ - Fix X11 coordinate overflows with huge borders.
+ - Improve CSS font parsing.
+ - Enable font face setting via <font> element.
+ - Ignore XML comment markers in CSS.
+ - Split up long lines in plain.cc to avoid X11 coordinate overflows.
+ - Fix user agent style for nested <ul>.
+ - Add support for CSS property list-style-position.
+ - Support border-width: thin | medium | thick.
+ - Fix CSS_SHORTHAND_DIRECTIONS case in CssParser.
+ - Add quirk to reset font properties in tables (fixes e.g. gmail).
+ Patches: Johannes Hofmann
++- Cleaned up system includes in dpid directory.
+ - Fixed CustProgressBox() for systems without weak symbols.
+ - Handle signed chars. Added dIsspace() and dIsalnum() to dlib.
+ - Added a_Dpip_get_attr_l() to DPIP's API.
+ - Changed the CCCs to build in one step (for both HTTP and DPI). This
+ is simpler and helps to avoid race conditions.
+ - Updated CCCwork.txt to the new scheme.
+ - Fixed a bug with OPTION element (it was parsing entities twice).
+ - Bugfix: remove the empty cache entry lingering after connection abort.
+ - Switched capi to use dlib's Dlist instead of a_List_* methods.
+ - Remove empty cache entries on Stop-button press and new link request!
+ - Fixed URL unescaping in the datauri DPI.
+ - Changed and reimplemented the DPI API.
+ * Fixed bugs and updated all DPI programs:
+ * Reimplemented the file dpi using select(). No pthreads-based anymore.
+ * Fixed ftp dpi: downloads, streamed transfer, error feedback.
+ * Fixed a bug in dillo with lingering cache entries.
+ * Made dpidc a C language program.
+ * Made the internal dsh implementation use unique functions for read/write.
+ * Removed the write/fwrite mix in DPIP.
+ * Made the DPIP API token-based. Packet assembling is coded inside DPIP!
+ * Several cleanups and more error handling sprinkled all over too.
+ Patches: Jorge Arellano Cid
++- Fix segfault from AREA when MAP is missing name attribute.
+ - Fix image map coordinates when margin/border/padding present.
+ - Handle stylesheet @charset.
+ - Fix cache segfault when cache entry removed.
+ - Split words that contain whitespace as numeric character references.
+ - Allow linebreaks around Chinese/Japanese characters.
+ - Fix segfault in Html_parse_doctype (BUG#918).
+ - Change exit code used for bad command line argument.
+ - By default, do not use proxy for localhost (BUG 921).
+ - Fix scrolling for text search.
+ - Added 'save' key action (not bound by default).
+ - Tooltips
+ - Fix segfault when radio button lacks name attribute.
+ - Enable popup menu below bottom of page content (BUG#856).
+ - Handle JPEGs with CMYK color space.
+ - Allow keysyms in keysrc.
+ - Explicitly check installation bindir for dpid (BUG 930)
+ - General cookies overhaul.
+ Patches: corvid
++- Support for the letter-spacing property.
+ Patch: Johannes Hofmann, corvid
++- Fixed a bug in w3c_mode. In fact it wasn't working at all.
+ - Improve stylesheet menu.
+ Patches: Jeremy Henty
++- Limit number of simultaneous connections (BUG 685).
+ Patch: Johannes Hofmann, Jorge Arellano Cid
+
+-----------------------------------------------------------------------------
+
+dillo-2.1.1 [Jul 3, 2009]
+
++- Add additional size checks for images.
+ Patch: Jorge Arellano Cid, Johannes Hofmann, corvid
++- Fixed a bug in parsing RGB color values (CSS).
+ - Added support for css colors of the form rgb(255, 255, 255).
+ - Assert that SimpleVector size is positive.
+ Patches: Johannes Hofmann
++- Removed redundant system includes.
+ - Added the "nop" keybinding (nop = NO_OPERATION; cancels a default hook).
+ - Added 'stop' key action (not bound by default).
+ - Fixed segfault when URL is NULL and dpis can't be found.
+ Patches: place (AKA corvid)
++- Reduced 'warning: ignoring return value of ...'
+ Patch: Michal Nowak, Jorge Arellano Cid
++- Check chdir() return code in Paths::init.
+ - Removed return from a_Nav_unref_buf()
+ - Do not build proto.c (file is empty); GCC warning
+ Patches: Michal Nowak
+
+-----------------------------------------------------------------------------
+
+dillo-2.1 [Jun 15, 2009]
+
++- Added ipv6 addresses iteration and ipv4 fallback.
+ Patch: James Turner, Jorge Arellano Cid
++- Added support for numeric IPv6 addresses entered into the url bar.
+ - Made the DNS resolver report in numeric address notation.
+ - Used the URL authority part instead of stripped default port in HTTP query.
+ - Fixed Bookmarks modify's HTML so it wraps nicely on handhelds.
+ Patches: Justus Winter
++- Implemented "search previous" in string searches.
+ Patch: João Ricardo Lourenço
++- Fix for file inputs without values (forms).
+ - Tuned input width a bit.
+ - Cleaned up resource embedding (forms)
+ - Made cookierc parsing more robust.
+ - Switched a_UIcmd_save() to take its URL from history (not location bar).
+ - Set prefs.vw_fontname as default font for the UI.
+ - Fix: recover page focus when clicking outside of a widget.
+ - Fixed a segfault bug in the test/ directory.
+ - Set middle click to submit in a new TAB. (Helps to keep form data!)
+ - Added support for the Q element. BUG#343
+ - Cleaned up Html_pop_tag().
+ - Ported the command line interface from dillo1
+ - Switched file dpi error messages to HTML.
+ - Added a right-click menu to form controls (show hiddens, submit, reset)
+ - Remove now-redundant generate_submit pref
+ - Added the "http_language" dillorc option for setting HTTP's Accept-Language.
+ - Refactored prefs.c to a much smaller size!
+ - Fixed a SEGFAULT bug on redirections without Location.
+ - Obey SELECT's size attribute.
+ - Replace image loading button and page menu option with a tools menu option.
+ - Implemented the "overline" text-decoration.
+ - Enhanced and cleaned up text decorations for SUB and SUP.
+ - Added "View Stylesheets" to the page menu.
+ - Remove standard_widget_colors dillorc option.
+ - Added dillo(1) man page.
+ - Proxy support for HTTPS.
+ - System config files have moved to sysconfdir/dillo/
+ - Add keysrc.
+ Patches: place (AKA corvid)
++- Switched SSL-enabled to configure.in (./configure --enable-ssl).
+ - Standardised the installation of dpid/dpidrc with auto* tools.
+ - Set the ScrollGroup as the resizable widget in downloads dpi.
+ - Cleaned up and normalized D_SUN_LEN usage.
+ - Fixed incorrect use of VOIDP2INT in Dialog_user_password_cb().
+ - Ensure that the dlib dStr* functions are used everywhere.
+ - Fixed a memory leak in Html_tag_open_link().
+ - Fixed a memory leak in Klist().
+ - Fix the comment for DLWin::del() (dpi/downloads.cc).
+ - Removed redundant caller NULL checks already in the API.
+ Patches: Jeremy Henty
++- Implemented Basic authentication!
+ Patch: Jeremy Henty, Jorge Arellano Cid
++- Added "-fno-rtti -fno-exceptions" to CXXFLAGS (reduces binary size).
+ Patch: Jorge Arellano Cid, place (AKA corvid)
++- Allowed compilation with older machines by removing a few C99isms.
+ - Added use of inttypes.h when stdint.h isn't found.
+ Patches: Dan Fandrich
++- Reduced warnings with gcc-4.3.
+ Patch: Thomas Orgis
++- Made the parser recognize "[^ ]/>"-terminated XML elements.
+ - Implemented basic CSS infrastructure.
+ - Brought in Sebastian's CSS parser from dillo-0.8.0-css-3.
+ - Read user style from ~/.dillo/style.css.
+ - Added support for descendant and child selectors.
+ - Improved CSS selector matching performance using hash tables.
+ - Support selector specificity.
+ - Add support for font-size and font-weight enum values.
+ - Added "font_max_size", "font_min_size" dillorc options.
+ - Add workaround for fltk bug #2062.
+ - Reduce number of styleEngine::style0() calls.
+ - Replace bg_color dillorc option.
+ - Remove text_color, link_color, and force_my_colors dillorc options.
+ - Fix CSS string parsing bug.
+ - Replace visited_color dillorc option.
+ - Add support for negative numbers in CSS parser.
+ - Fix allow_white_bg dillorc option.
+ - Load <style></style> content only if applicable.
+ - Allow negative values for specific CSS properties only.
+ - Disable negative margins for now as dw/* does not support them yet.
+ - Support CSS @import directive.
+ - Disable form widgets while stylesheets are loading.
+ - Fix image scaling on reload with border, margin, or padding > 0.
+ - Implement --xid command line option (used by claws mail client).
+ - Make tab expansion in plain text utf8 aware.
+ Patches: Johannes Hofmann
++- Updated the GPL copyright note in the source files.
+ Patch: Detlef Riekenberg
++- Implemented a close-tab button for the GUI.
+ Patch: João Ricardo Lourenço, Jorge Arellano Cid
++- Added the "middle_click_drags_page" dillorc option.
+ Patch: Jorge Arellano Cid, Thomas Orgis
++- Added configurable keybindings! (in ~/.dillo/keysrc)
+ Patch: Jorge Arellano Cid, Tim Nieradzik, place (AKA corvid)
++- Fixed a memory leak with DilloImage structures.
+ Patch: Johannes Hofmann, place (AKA corvid)
++- Set the File menu label to hide when the File menu-button is shown.
+ - Set a new iconv() test in configure.in.
+ - Allowed the rc parser to skip whitespace around the equal sign.
+ - Fixed the parser not to call Html_tag_close_* functions twice.
+ - Implemented loading of remote CSS Stylesheet.
+ - Made a big cleanup of cache.c WRT charset decoding (fixes bugs).
+ - Made an extensive cleanup/fixup of the whole image handling process.
+ - Implemented the tools button with a couple CSS options.
+ - Removed the nav.h dependency from html.cc
+ - Made the repush() operation more general and suited for CSS use.
+ - Fixed collapsing of whitespace entities in HTML mode.
+ - Updated the URL resolver to comply with RFC-3986.
+ - Fixed handling of META's content-type with no MIME type (e.g. only charset).
+ - Added support for a quoted URL in META refresh.
+ - Added instant client-side redirects (aka. zero-delay META refresh).
+ Patches: Jorge Arellano Cid
+
+dw
+
++- Moved clicked from ButtonResource to Resource.
+ Patch: place (AKA corvid)
++- Cleaned up unused code in fltkviewbase.
+ Patch: Johannes Hofmann
++- Added lout/msg.h and normalized debug messages to use it.
+ Patch: Jorge Arellano Cid
+
+-----------------------------------------------------------------------------
+
+dillo-2.0 [Oct 14, 2008]
+- Ported Dillo from GTK1 to FLTK2.
- Ported a susbstantial part of the code from C to C++ (FLTK2 is in C++).
@@ -177,9 +415,6 @@ dillo-2.0
- Replaced the findtext dialog with an in-window widget!
Patches: Justus Winter
- TODO:
-
- - test no_proxy (set a list in dillorc).
-----------------------------------------------------------------------------
dw
diff --git a/Doxyfile b/Doxyfile
index 8aae5d7f..971cf7be 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,4 +1,4 @@
-# Doxyfile 1.3.7
+# Doxyfile 1.5.8
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
@@ -14,6 +14,14 @@
# Project related configuration options
#---------------------------------------------------------------------------
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
@@ -30,14 +38,14 @@ PROJECT_NUMBER =
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
-OUTPUT_DIRECTORY =
+OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 2 levels of 10 sub-directories under the output directory of each output
+# 4096 sub-directories (in 2 levels) under the output directory of each output
# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of source
-# files, where putting all generated files in the same directory would otherwise
-# cause performance problems for the file system.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
CREATE_SUBDIRS = NO
@@ -45,23 +53,15 @@ CREATE_SUBDIRS = NO
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# The default language is English, other supported languages are:
-# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
-# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
-# (Japanese with English messages), Korean, Korean-en, Norwegian, Polish, Portuguese,
-# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek,
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish,
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene,
+# Spanish, Swedish, and Ukrainian.
OUTPUT_LANGUAGE = English
-# This tag can be used to specify the encoding used in the generated output.
-# The encoding is not always determined by the language that is chosen,
-# but also whether or not the output is meant for Windows or non-Windows users.
-# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
-# forces the Windows encoding (this is the default for the Windows binary),
-# whereas setting the tag to NO uses a Unix-style encoding (the default for
-# all platforms other than Windows).
-
-USE_WINDOWS_ENCODING = NO
-
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
@@ -79,11 +79,12 @@ REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator
# that is used to form the text in various listings. Each string
# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is used
-# as the annotated text. Otherwise, the brief description is used as-is. If left
-# blank, the following values are used ("$name" is automatically replaced with the
-# name of the entity): "The $name class" "The $name widget" "The $name file"
-# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
ABBREVIATE_BRIEF =
@@ -93,10 +94,10 @@ ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
-# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
-# members of a class in the documentation of that class as if those members were
-# ordinary class members. Constructors, destructors and assignment operators of
-# the base classes will not be shown.
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
@@ -133,11 +134,19 @@ SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like the Qt-style comments (thus requiring an
-# explicit @brief command for a brief description.
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
JAVADOC_AUTOBRIEF = NO
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
# treat a multi-line C++ special comment block (i.e. a block of //! or ///
# comments) as a brief description. This used to be the default behaviour.
@@ -146,25 +155,17 @@ JAVADOC_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
-# If the DETAILS_AT_TOP tag is set to YES then Doxygen
-# will output the detailed description near the top, like JavaDoc.
-# If set to NO, the detailed description appears after the member
-# documentation.
-
-DETAILS_AT_TOP = YES
-
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# re-implements.
INHERIT_DOCS = YES
-# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
-# tag is set to YES, then doxygen will reuse the documentation of the first
-# member in the group (if any) for the other members of the group. By default
-# all members of a group must be documented explicitly.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
-DISTRIBUTE_GROUP_DOC = NO
+SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
@@ -180,20 +181,79 @@ TAB_SIZE = 8
ALIASES =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
-# only. Doxygen will then generate output that is more tailored for C.
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
# For instance, some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = NO
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
-# only. Doxygen will then generate output that is more tailored for Java.
-# For instance, namespaces will be presented as packages, qualified scopes
-# will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
OPTIMIZE_OUTPUT_JAVA = NO
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a
# subgroup of that type (e.g. under the Public Functions section). Set it to
@@ -202,6 +262,32 @@ OPTIMIZE_OUTPUT_JAVA = NO
SUBGROUPING = YES
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -236,6 +322,14 @@ EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
@@ -276,7 +370,7 @@ INTERNAL_DOCS = NO
# file names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
-# users are advised to set this option to NO.
+# and Mac users are advised to set this option to NO.
CASE_SENSE_NAMES = YES
@@ -311,11 +405,17 @@ SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
# sorted by fully-qualified names, including namespaces. If set to
# NO (the default), the class list will be sorted only by class name,
# not including the namespace part.
-# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the
# alphabetical list.
@@ -366,6 +466,44 @@ MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
@@ -394,10 +532,20 @@ WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text.
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
WARN_FORMAT = "$file:$line: $text"
@@ -416,16 +564,26 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT =
+INPUT =
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
-# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
-FILE_PATTERNS = *.cc *.hh *.doc
+FILE_PATTERNS = *.cc \
+ *.hh \
+ *.doc
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
@@ -437,19 +595,35 @@ RECURSIVE = YES
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
-EXCLUDE = dlib dpi dpid dpip src test
+EXCLUDE = dlib \
+ dpi \
+ dpid \
+ dpip \
+ src \
+ test
-# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
-# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories.
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
EXCLUDE_PATTERNS =
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
@@ -481,10 +655,23 @@ IMAGE_PATH = doc
# by executing (via popen()) the command <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
# input file. Doxygen will then use the output that the filter program writes
-# to standard output.
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
INPUT_FILTER =
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse (i.e. when SOURCE_BROWSER is set to YES).
@@ -513,18 +700,34 @@ INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# If the REFERENCED_BY_RELATION tag is set to YES
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
-# If the REFERENCES_RELATION tag is set to YES (the default)
+# If the REFERENCES_RELATION tag is set to YES
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
@@ -602,9 +805,43 @@ HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
@@ -629,6 +866,12 @@ HHC_LOCATION =
GENERATE_CHI = NO
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
# controls whether a binary table of contents is generated (YES) or a
# normal table of contents (NO) in the .chm file.
@@ -640,6 +883,55 @@ BINARY_TOC = NO
TOC_EXPAND = NO
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
@@ -651,12 +943,20 @@ DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
-# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
-# generated containing a tree-like index structure (just like the one that
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+,
# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are
-# probably better off using the HTML help feature.
+# probably better off using the HTML help feature. Other possible values
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
GENERATE_TREEVIEW = NO
@@ -666,6 +966,14 @@ GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
@@ -878,8 +1186,10 @@ GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader. This is useful
-# if you want to understand what is going on. On the other hand, if this
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
# tag is set to NO the size of the Perl module output will be much smaller
# and Perl will parse it just the same.
@@ -911,7 +1221,7 @@ MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+# PREDEFINED and EXPAND_AS_DEFINED tags.
EXPAND_ONLY_PREDEF = NO
@@ -937,7 +1247,9 @@ INCLUDE_FILE_PATTERNS =
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed.
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
PREDEFINED =
@@ -951,8 +1263,8 @@ EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line, have an all uppercase name, and do not end with a semicolon. Such
-# function macros are typically used for boiler-plate code, and will confuse the
-# parser if not removed.
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
SKIP_FUNCTION_MACROS = YES
@@ -964,14 +1276,16 @@ SKIP_FUNCTION_MACROS = YES
# Optionally an initial location of the external documentation
# can be added for each tagfile. The format of a tag file without
# this location is as follows:
-# TAGFILES = file1 file2 ...
+#
+# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
-# TAGFILES = file1=loc1 "file2 = loc2" ...
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
# where "loc1" and "loc2" can be relative or absolute paths or
# URLs. If a location is present for each tag, the installdox tool
-# does not have to be run to correct the links.
-# Note that each tag file must have a unique name
-# (where the name does NOT include the path)
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
# If a tag file is not located in the directory in which doxygen
# is run, you must also specify the path to the tagfile here.
@@ -1004,13 +1318,23 @@ PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
-# super classes. Setting the tag to NO turns the diagrams off. Note that this
-# option is superseded by the HAVE_DOT option below. This is only a fallback. It is
-# recommended to install and use dot, since it yields more powerful graphs.
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
CLASS_DIAGRAMS = YES
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
@@ -1023,7 +1347,29 @@ HIDE_UNDOC_RELATIONS = YES
# have no effect if this option is set to NO (the default)
HAVE_DOT = YES
-#HAVE_DOT = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
@@ -1039,6 +1385,11 @@ CLASS_GRAPH = NO
COLLABORATION_GRAPH = NO
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
@@ -1064,27 +1415,42 @@ INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will
-# generate a call dependency graph for every global function or class method.
-# Note that enabling this option will significantly increase the time of a run.
-# So in most cases it will be better to enable call graphs for selected
-# functions only using the \callgraph command.
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
CALL_GRAPH = NO
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = NO
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are png, jpg, or gif
+# generated by dot. Possible values are png, jpg, or gif
# If left blank png will be used.
DOT_IMAGE_FORMAT = png
# The tag DOT_PATH can be used to specify the path where the dot tool can be
-# found. If left blank, it is assumed the dot tool can be found on the path.
+# found. If left blank, it is assumed the dot tool can be found in the path.
DOT_PATH =
@@ -1092,35 +1458,43 @@ DOT_PATH =
# contain dot files that are included in the documentation (see the
# \dotfile command).
-DOTFILE_DIRS =
+DOTFILE_DIRS =
-# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than
-# this value, doxygen will try to truncate the graph, so that it fits within
-# the specified constraint. Beware that most browsers cannot cope with very
-# large images.
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-MAX_DOT_GRAPH_WIDTH = 1024
-
-# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
-# (in pixels) of the graphs generated by dot. If a graph becomes larger than
-# this value, doxygen will try to truncate the graph, so that it fits within
-# the specified constraint. Beware that most browsers cannot cope with very
-# large images.
-
-MAX_DOT_GRAPH_HEIGHT = 20480
+DOT_GRAPH_MAX_NODES = 50
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes that
-# lay further from the root node will be omitted. Note that setting this option to
-# 1 or 2 may greatly reduce the computation time needed for large code bases. Also
-# note that a graph may be further truncated if the graph's image dimensions are
-# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT).
-# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
MAX_DOT_GRAPH_DEPTH = 0
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
@@ -1130,11 +1504,11 @@ GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermediate dot files that are used to generate
# the various graphs.
-
+
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine
+# Options related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
diff --git a/README b/README
index 80f697fb..ecff6635 100644
--- a/README
+++ b/README
@@ -1,32 +1,29 @@
-=======
- Dillo
-=======
+===================
+ Dillo web browser
+===================
- This is a beta release of the next generation of the Dillo web
-browser. The code underwent a major rewrite: significant parts of
-the codebase were ported to C++, and the rendering engine now
-uses the FLTK2 library instead of GTK1.
+ Dillo 2.2 features a major overhaul of the cookies subsystem,
+a reimplementation of the Dillo Plugin (DPI) API, a configurable
+connection limit, and various CSS improvements.
-Our users will surely enjoy this new release as it will give them
-the same things they're accustomed plus tabbed browsing,
-antialiasing, different character sets, accepting compressed
-pages, control over image loading, smaller footprint, fewer
-dependencies, better table rendering, bugfixes, improved GUI, ...
-In brief, a better dillo.
+ This release is part of the Dillo2 series. In Dillo2, significant
+parts of the codebase were ported to C++, and the rendering engine was
+modified to use the FLTK2 library rather than GTK1.
-
- Here's a list of some well known problems:
+ Here's a list of some well-known problems:
* no FRAMES rendering
* no https (there's a barebones prototype).
+
-----
FLTK2
-----
- You can get the fltk2 library from fltk.org.
-The recommended version is >= r6403. e.g. in:
+ The FLTK2 library is statically linked into Dillo2.
+ You can get it from fltk.org.
+The recommended version is >= r6916. e.g. in:
- http://fltk.org/software.php?VERSION=2.0.x-r6403
+ http://fltk.org/software.php?VERSION=2.0.x-r6916
------------
@@ -67,7 +64,7 @@ Solaris
at link time.
-
+
Jorge.-
(jcid@dillo.org)
-Oct, 2008
+June, 2009
diff --git a/configure.in b/configure.in
index 2741ce33..a7fef723 100644
--- a/configure.in
+++ b/configure.in
@@ -5,8 +5,9 @@ AC_INIT(src/dillo.cc)
dnl Detect the canonical host and target build environment
AC_CANONICAL_SYSTEM
-AM_INIT_AUTOMAKE(dillo, 2.0)
+AM_INIT_AUTOMAKE(dillo, 2.2)
AM_CONFIG_HEADER(config.h)
+sysconfdir=${sysconfdir}/${PACKAGE}
dnl Options
@@ -21,6 +22,8 @@ AC_ARG_ENABLE(insure, [ --enable-insure Try to compile and run with Ins
, enable_insure=no)
AC_ARG_ENABLE(ansi, [ --enable-ansi Try to compile and run with ANSI flags],
, enable_ansi=no)
+AC_ARG_ENABLE(ssl, [ --enable-ssl Enable ssl, https (ALPHA CODE)],
+ , enable_ssl=no)
AC_ARG_ENABLE(ipv6, [ --enable-ipv6 Build with support for IPv6], , )
AC_ARG_ENABLE(cookies,[ --disable-cookies Don't compile support for cookies],
, enable_cookies=yes)
@@ -30,8 +33,6 @@ AC_ARG_ENABLE(jpeg, [ --disable-jpeg Disable support for JPEG images
enable_jpeg=$enableval, enable_jpeg=yes)
AC_ARG_ENABLE(gif, [ --disable-gif Disable support for GIF images],
enable_gif=$enableval, enable_gif=yes)
-AC_ARG_ENABLE(ssl, [ --disable-ssl Disable ssl features (eg. https)],
- enable_ssl=$enableval, enable_ssl=yes)
AC_ARG_ENABLE(threaded-dns,[ --disable-threaded-dns Disable the advantage of a reentrant resolver library],
enable_threaded_dns=$enableval, enable_threaded_dns=yes)
AC_ARG_ENABLE(rtfl, [ --enable-rtfl Build with rtfl messages])
@@ -70,6 +71,8 @@ cat >d_size.h <<_______EOF
#if HAVE_STDINT_H == 0
#include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+#include <inttypes.h>
#else
typedef signed $gint16 int16_t;
typedef unsigned $gint16 uint16_t;
@@ -132,7 +135,7 @@ ac_cv_socklen_t="size_t"
)
AC_MSG_RESULT($ac_cv_socklen_t)
if test "$ac_cv_socklen_t" != "socklen_t"; then
- AC_DEFINE_UNQUOTED(socklen_t, $ac_cv_socklen_t,
+ AC_DEFINE_UNQUOTED([socklen_t], [$ac_cv_socklen_t],
[Define the real type of socklen_t])
fi
@@ -180,7 +183,7 @@ if test "x$enable_jpeg" = "xyes"; then
fi
if test "x$jpeg_ok" = "xyes"; then
- AC_DEFINE([ENABLE_JPEG], [], [Enable JPEG images])
+ AC_DEFINE([ENABLE_JPEG], [1], [Enable JPEG images])
fi
dnl -------------
@@ -210,7 +213,10 @@ if test "x$enable_png" = "xyes"; then
dnl Check if the user hasn't set the variable $PNG_CONFIG
if test -z "$PNG_CONFIG"; then
- PNG_CONFIG=`which libpng12-config`
+ PNG_CONFIG=`which libpng14-config`
+ if test -z "$PNG_CONFIG"; then
+ PNG_CONFIG=`which libpng12-config`
+ fi
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`which libpng-config`
fi
@@ -233,6 +239,7 @@ dnl For debugging and to be user friendly
AC_MSG_CHECKING([for libpng version])
png_version=`$PNG_CONFIG --version`
case $png_version in
+ 1.4.*) AC_MSG_RESULT([$png_version (newer version)]) ;;
1.2.*) AC_MSG_RESULT([$png_version (newer version)]) ;;
1.0.*) AC_MSG_RESULT([$png_version (older version)]) ;;
*) AC_MSG_RESULT([ERROR]) ;;
@@ -250,7 +257,7 @@ dnl Try to find libpng even though libpng-config wasn't found
if test "x$png_ok" = "xyes"; then
old_libs="$LIBS"
- AC_CHECK_LIB(png, png_check_sig, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
+ AC_CHECK_LIB(png, png_sig_cmp, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
LIBS="$old_libs"
if test "x$png_ok" = "xyes"; then
@@ -265,12 +272,12 @@ dnl Try to find libpng even though libpng-config wasn't found
fi
if test "x$png_ok" = "xyes"; then
- AC_DEFINE([ENABLE_PNG], [], [Enable PNG images])
+ AC_DEFINE([ENABLE_PNG], [1], [Enable PNG images])
fi
dnl Check if support for GIF images should be compiled in
if test "x$enable_gif" = "xyes"; then
- AC_DEFINE([ENABLE_GIF], [], [Enable GIF images])
+ AC_DEFINE([ENABLE_GIF], [1], [Enable GIF images])
fi
dnl --------------------------
@@ -288,13 +295,14 @@ if test "x$enable_ssl" = "xyes"; then
if test "x$ssl_ok" = "xyes"; then
LIBSSL_LIBS="-lcrypto -lssl"
+ AC_MSG_WARN([*** Enabling ssl support. THIS IS ALPHA CODE!***])
else
AC_MSG_WARN([*** No libssl found. Disabling ssl support.***])
fi
fi
if test "x$ssl_ok" = "xyes"; then
- AC_DEFINE([ENABLE_SSL], [], [Enable SSL support])
+ AC_DEFINE([ENABLE_SSL], [1], [Enable SSL support])
fi
dnl --------------------------------------------------------------
@@ -331,6 +339,7 @@ if test "x$iconv_ok" = "xyes"; then
LIBS="$old_libs $LIBICONV_LIBS"
old_cflags="$CFLAGS"
CFLAGS="$CFLAGS -Werror"
+ AC_LANG_PUSH([C++])
AC_TRY_COMPILE([#include <iconv.h>],
[
const char *inPtr;
@@ -340,6 +349,7 @@ if test "x$iconv_ok" = "xyes"; then
iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
],
iconv_old=yes,iconv_old=no)
+ AC_LANG_POP([C++])
LIBS="$old_libs"
CFLAGS="$old_cflags"
@@ -368,6 +378,10 @@ case $target in
AC_MSG_WARN([*** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***])
;;
+ *-*-minix*)
+ AC_MSG_NOTICE([Minix detected, skipping pthread detection])
+ ;;
+
*)
AC_MSG_CHECKING(whether threads work with -pthread)
LDSAVEFLAGS=$LDFLAGS
@@ -407,6 +421,7 @@ dnl --------------------
dnl
if test "x$enable_cookies" = "xno" ; then
CFLAGS="$CFLAGS -DDISABLE_COOKIES"
+ CXXFLAGS="$CXXFLAGS -DDISABLE_COOKIES"
fi
if test "x$enable_ipv6" = "xyes" ; then
CFLAGS="$CFLAGS -DENABLE_IPV6"
@@ -450,6 +465,9 @@ if eval "test x$GCC = xyes"; then
if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -Wall"
fi
+ if test "`echo $CFLAGS | grep -e '-W ' -e '-W$' 2> /dev/null`" = ""; then
+ CFLAGS="$CFLAGS -W"
+ fi
if test "`echo $CFLAGS | grep '\-W[^a]' 2> /dev/null`" = ""; then
if test "`$CC -v 2>&1 | grep 'version 3'`" != ""; then
CFLAGS="$CFLAGS -W -Wno-unused-parameter"
@@ -473,7 +491,10 @@ dnl -----------
dnl CXX options
dnl -----------
dnl
-CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter"
+
+if eval "test x$GCC = xyes"; then
+ CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions"
+fi
AC_SUBST(LIBJPEG_LIBS)
AC_SUBST(LIBJPEG_LDFLAGS)
diff --git a/dillorc b/dillorc
index 05debda6..ba132ba0 100644
--- a/dillorc
+++ b/dillorc
@@ -15,9 +15,23 @@
#geometry=780x580
# Change this if you want to have text-only browsing from the start.
-# (there's a toggle button near the bug meter to change this on-the-fly)
+# (While browsing, this can be changed from the tools/settings menu.)
#load_images=YES
+# Change this if you want to disable loading of CSS stylesheets initially.
+# (While browsing, this can be changed from the tools/settings menu.)
+#load_stylesheets=YES
+
+# Change this if you want to disable parsing of embedded CSS initially.
+# (While browsing, this can be changed from the tools/settings menu.)
+#parse_embedded_css=YES
+
+# How should Dillo restrict automatic requests (e.g., redirections,
+# pages containing images or stylesheets)?
+# allow_all
+# same_domain : Permit www.example.org to load an image from img.example.org,
+# but not from the unrelated ad.doubleclick.net.
+#filter_auto_requests=same_domain
# Change the buffering scheme for drawing
# 0 no double buffering - useful for debugging
@@ -32,32 +46,35 @@
# RENDERING SECTION
#-------------------------------------------------------------------------
-# Fontname for variable width rendering (most of the text).
-# - some fonts may slow down rendering.
-# - try to tune a fontname/font_factor combination.
-# Ex. {helvetica, lucida, times, "new century schoolbook", utopia, ...}
-# vw_fontname="new century schoolbook"
-# vw_fontname="helvetica"
-# vw_fontname="times"
-# vw_fontname="Bitstream vera Serif"
-# vw_fontname="arial"
-#vw_fontname="DejaVu Sans"
-
-# Fontname for fixed width rendering (mainly text quoted with <pre>)
-# fw_fontname=courier
-# fw_fontname="Bitstream Vera Sans Mono"
-# fw_fontname="Andale Mono"
-#fw_fontname="DejaVu Sans Mono"
-
-# All fontsizes are scaled by this value
+# Default fonts:
+#
+# If FLTK2 has been configured with Xft enabled (the default), you can
+# use scalable fonts such as DejaVu or Liberation (try running "fc-list").
+#font_serif="DejaVu Serif"
+#font_sans_serif="DejaVu Sans"
+#font_cursive="URW Chancery L"
+#font_fantasy="DejaVu Sans"
+#font_monospace="DejaVu Sans Mono"
+#
+# Otherwise, use bitmapped fonts like the following (for a list, try running
+# "xlsfonts -fn *-iso10646-1 | grep -v -e -0-0 | cut -d - -f 3 | sort | uniq").
+# font_serif="times"
+# font_sans_serif="helvetica"
+# font_cursive="helvetica"
+# font_fantasy="helvetica"
+# font_monospace="courier"
+
+# All font sizes are scaled by this value
# font_factor=1.5
#font_factor=1.0
-# Show tooltip popup for images?
-# Note: We use the "title" attribute and not "alt".
-# More info at: http://bugzilla.mozilla.org/show_bug.cgi?id=25537
-# *** NOT HOOKED UP YET ***
-#
+# Maximum font size in pixels
+#font_max_size=100
+
+# Minimum font size in pixels
+#font_min_size=6
+
+# Show tooltip popups for UI and for HTML title attributes
#show_tooltip=YES
# Set this to YES if you want to limit the word wrap width to the viewport
@@ -93,13 +110,28 @@
# Set the URL used by the web search dialog.
# "%s" is replaced with the search keywords separated by '+'.
+# search_url="http://www.wikipedia.org/wiki/Special:Search?search=%s"
# search_url="http://search.lycos.com/?query=%s"
# search_url="http://www.alltheweb.com/search?cat=web&query=%s"
#search_url="http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
+# If set, dillo will ask web servers to send pages in this language.
+# This setting does NOT change dillo's user interface.
+# Format explained: www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
+# Language-REGION values: www.iana.org/assignments/language-subtag-registry
+# (by default, no Accept-Language header is sent)
+# http_language="de"
+# http_language="pt-BR"
+# http_language="vi,de-CH,de;q=0.5,th;q=0.3"
+
+# Maximum number of simultaneous TCP connections to a single server or proxy.
+# http_max_conns=6
+
# Set the proxy information for http.
-# WARNING: dillo uses wget for ftp and https. To use a proxy for those
-# protocols, you will need to configure wget.
+# Note that the http_proxy environment variable overrides this setting.
+# WARNING: FTP and downloads plugins use wget. To use a proxy with them,
+# you will need to configure wget accordingly. See
+# http://www.gnu.org/software/wget/manual/html_node/Proxies.html
# http_proxy="http://localhost:8080/"
#(by default, no proxy is used)
@@ -110,7 +142,7 @@
# Set the domains to access without proxy
# no_proxy = ".hola.com .mynet.cl .hi.de"
-#(by default, no proxy is used)
+#no_proxy="localhost 127.0.0.1"
# Set the HTTP Referer (sic) header.
# Note that there is no option to reveal the page that you came from because it
@@ -121,39 +153,30 @@
# path : Send the requested URI's host and path.
#http_referer=host
+# Set the HTTP User-Agent header.
+# This can be useful for privacy and for working around servers who think
+# Dillo is less capable than it really is. However, if you pretend to use a
+# different browser, servers may send you pages that work with the features
+# and bugs of that other browser -- or even disallow access in cases like
+# wget or googlebot. Remember this before submitting bug reports.
+#
+# See http://zytrax.com/tech/web/browser_ids.htm for a compilation of strings.
+#
+# http_user_agent="Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6"
+# http_user_agent="Wget/1.11.4"
+#The default is Dillo/(current version number)
#-------------------------------------------------------------------------
# COLORS SECTION
#-------------------------------------------------------------------------
-# Here we can use the HTML color names or C syntax.
-
-# Set the background color
-# bg_color=gray
-# bg_color=0xd6d6c0
-#bg_color=0xdcd1ba
-
-# Set the text color
-#text_color=black
-
-# Set the link color
-#link_color=blue
-
# If your eyes suffer with white backgrounds, change this.
#allow_white_bg=YES
-# Use the same colors with all documents?
-#force_my_colors=NO
-
# When set to YES, the page author's visited link color may be overridden
# to allow better contrast with text/links/background
#contrast_visited_color=YES
-# Colors for widgets inside forms.
-# YES = toolkit's default colors, NO = use page's text and background color.
-# ("NO" looks less traditional but more stylish).
-#standard_widget_colors=NO
-
#-------------------------------------------------------------------------
# USER INTERFACE SECTION
@@ -166,8 +189,8 @@
# large : Traditional
# panel_size=tiny
# panel_size=small
-# panel_size=medium
-#panel_size=large
+#panel_size=medium
+# panel_size=large
#small_icons=NO
@@ -179,10 +202,12 @@
#show_save=YES
#show_stop=YES
#show_bookmarks=YES
+#show_tools=YES
#show_filemenu=YES
#show_clear_url=YES
#show_url=YES
#show_search=YES
+#show_help=YES
#show_progress_box=YES
# Start dillo with the panels hidden?
@@ -194,15 +219,15 @@
# useful to keep away from the mouse by forcing enter to submit.
#enterpress_forces_submit=NO
-# Some forms lack a submit button, and dillo can generate a custom one
-# internally. Unfortunately there's no guarantee for it to work. :(
-# (my experience is that forms that lack a submit rely on Javascript)
-#generate_submit=NO
-
# A mouse's middle click over a link opens a new Tab.
# If you prefer to open a new Window instead, set it to NO.
#middle_click_opens_new_tab=YES
+# Mouse middle click by default drives drag-scrolling.
+# To paste an URL into the window instead of scrolling, set it to NO.
+# Note: You could always paste the URL onto the URL box clear button.
+#middle_click_drags_page=YES
+
# Focus follows new Tabs.
# You can hold SHIFT to temporarily revert this behaviour.
#focus_new_tab=YES
diff --git a/dlib/dlib.c b/dlib/dlib.c
index f92f64e7..b87d0ce1 100644
--- a/dlib/dlib.c
+++ b/dlib/dlib.c
@@ -27,6 +27,15 @@
#include "dlib.h"
+static bool_t dLib_show_msg = TRUE;
+
+/* dlib msgs go to stderr to avoid problems with filter dpis */
+#define DLIB_MSG(...) \
+ D_STMT_START { \
+ if (dLib_show_msg) \
+ fprintf(stderr, __VA_ARGS__); \
+ } D_STMT_END
+
/*
*- Memory --------------------------------------------------------------------
*/
@@ -115,8 +124,8 @@ char *dStrstrip(char *s)
int len;
if (s && *s) {
- for (p = s; isspace(*p); ++p);
- for (len = strlen(p); len && isspace(p[len-1]); --len);
+ for (p = s; dIsspace(*p); ++p);
+ for (len = strlen(p); len && dIsspace(p[len-1]); --len);
if (p > s)
memmove(s, p, len);
s[len] = 0;
@@ -160,18 +169,20 @@ char *dStrsep(char **orig, const char *delim)
char *dStristr(const char *haystack, const char *needle)
{
int i, j;
-
- for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
- if (tolower(haystack[i]) == tolower(needle[j])) {
- ++j;
- } else if (j) {
- i -= j;
- j = 0;
- }
-
- if (!needle[j]) /* Got all */
- return (char *)(haystack + i - j);
- return NULL;
+ char *ret = NULL;
+
+ if (haystack && needle) {
+ for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
+ if (tolower(haystack[i]) == tolower(needle[j])) {
+ ++j;
+ } else if (j) {
+ i -= j;
+ j = 0;
+ }
+ if (!needle[j]) /* Got all */
+ ret = (char *)(haystack + i - j);
+ }
+ return ret;
}
@@ -204,10 +215,11 @@ static void dStr_resize(Dstr *ds, int n_sz, int keep)
*/
Dstr *dStr_sized_new (int sz)
{
+ Dstr *ds;
if (sz < 2)
sz = 2;
- Dstr *ds = dNew(Dstr, 1);
+ ds = dNew(Dstr, 1);
ds->str = NULL;
dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
return ds;
@@ -476,10 +488,11 @@ const char *dStr_printable(Dstr *in, int maxlen)
*/
Dlist *dList_new(int size)
{
+ Dlist *l;
if (size <= 0)
return NULL;
- Dlist *l = dNew(Dlist, 1);
+ l = dNew(Dlist, 1);
l->len = 0;
l->sz = size;
l->list = dNew(void*, l->sz);
@@ -756,9 +769,10 @@ void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
* - line is modified!
* - it skips blank lines and lines starting with '#'
*
- * Return value: 0 on successful value/pair, -1 otherwise
+ * Return value: 1 on blank line or comment, 0 on successful value/pair,
+ * -1 otherwise.
*/
-int dParser_get_rc_pair(char **line, char **name, char **value)
+int dParser_parse_rc_line(char **line, char **name, char **value)
{
char *eq, *p;
int len, ret = -1;
@@ -768,15 +782,22 @@ int dParser_get_rc_pair(char **line, char **name, char **value)
*name = NULL;
*value = NULL;
dStrstrip(*line);
- if (*line[0] != '#' && (eq = strchr(*line, '='))) {
+ if (!*line[0] || *line[0] == '#') {
+ /* blank line or comment */
+ ret = 1;
+ } else if ((eq = strchr(*line, '='))) {
/* get name */
- for (p = *line; *p && *p != '=' && !isspace(*p); ++p);
+ for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
*p = 0;
*name = *line;
+ /* skip whitespace */
+ if (p < eq)
+ for (++p; dIsspace(*p); ++p);
+
/* get value */
if (p == eq) {
- for (++p; isspace(*p); ++p);
+ for (++p; dIsspace(*p); ++p);
len = strlen(p);
if (len >= 2 && *p == '"' && p[len-1] == '"') {
p[len-1] = 0;
@@ -791,6 +812,14 @@ int dParser_get_rc_pair(char **line, char **name, char **value)
}
/*
+ *- Dlib messages -------------------------------------------------------------
+ */
+void dLib_show_messages(bool_t show)
+{
+ dLib_show_msg = show;
+}
+
+/*
*- Misc utility functions ----------------------------------------------------
*/
@@ -825,6 +854,9 @@ char *dGethomedir ()
} else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
+ } else {
+ DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
+ homedir = dStrdup("/");
}
}
return homedir;
@@ -844,10 +876,10 @@ char *dGetline (FILE *stream)
dReturn_val_if_fail (stream, 0);
dstr = dStr_sized_new(64);
- while((ch = fgetc(stream)) != EOF) {
+ while ((ch = fgetc(stream)) != EOF) {
if (ch == '\\') {
/* continue with the next line */
- while((ch = fgetc(stream)) != EOF && ch != '\n');
+ while ((ch = fgetc(stream)) != EOF && ch != '\n') ;
continue;
}
dStr_append_c(dstr, ch);
diff --git a/dlib/dlib.h b/dlib/dlib.h
index aa32beb4..ae9c7286 100644
--- a/dlib/dlib.h
+++ b/dlib/dlib.h
@@ -7,6 +7,7 @@
#include <string.h> /* for strerror */
#include <strings.h> /* for strcasecmp, strncasecmp (POSIX 2001) */
+#include "d_size.h"
#ifdef __cplusplus
extern "C" {
@@ -29,6 +30,10 @@ extern "C" {
#undef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+/* Handle signed char */
+#define dIsspace(c) isspace((uchar_t)(c))
+#define dIsalnum(c) isalnum((uchar_t)(c))
+
/*
*-- Casts -------------------------------------------------------------------
*/
@@ -55,6 +60,14 @@ void dFree (void *mem);
*/
#define D_STMT_START do
#define D_STMT_END while (0)
+#define dReturn_if(expr) \
+ D_STMT_START{ \
+ if (expr) { return; }; \
+ }D_STMT_END
+#define dReturn_val_if(expr,val) \
+ D_STMT_START{ \
+ if (expr) { return val; }; \
+ }D_STMT_END
#define dReturn_if_fail(expr) \
D_STMT_START{ \
if (!(expr)) { return; }; \
@@ -150,7 +163,12 @@ void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func);
/*
*- Parse function ------------------------------------------------------------
*/
-int dParser_get_rc_pair(char **line, char **name, char **value);
+int dParser_parse_rc_line(char **line, char **name, char **value);
+
+/*
+ *- Dlib messages -------------------------------------------------------------
+ */
+void dLib_show_messages(bool_t show);
/*
*- Misc utility functions ----------------------------------------------------
diff --git a/doc/CCCwork.txt b/doc/CCCwork.txt
new file mode 100644
index 00000000..1ea5d20e
--- /dev/null
+++ b/doc/CCCwork.txt
@@ -0,0 +1,153 @@
+Last review: August 04, 2009 --jcid
+
+
+----------------------------
+Internal working for the CCC
+----------------------------
+
+
+HTTP protocol
+-------------
+
+
+ Query: |
+ .
+ 1B --> 1B 1B --> 1B --> | -------------.
+ .----. .----. .----. . |
+I |Capi| |http| | IO | | |
+ '----' '----' '----' . |
+ 1F <-- 1F 1F <-- 1F | V
+ .
+ | [Server]
+ Answer: .
+
+ 2B --> 2B 2B --> 2B | |
+ .----. .----. .----. . |
+II |Capi| |Dpi | | IO | | |
+ '----' '----' '----' . |
+ 2F <-- 2F 2F <-- 2F <-- | <------------'
+ .
+ |
+
+* a_Capi_open_url() builds both the Answer and Query chains at
+once (Answer first then Query), to ensure a uniform structure
+that avoids complexity (e.g. race conditions).
+
+* Http_get() sets a callback for the DNS hostname resolve.
+Normally it comes later, but may also by issued immediately if
+the hostname is cached.
+
+* The socket FD is passed by means of OpSend by the http module
+once the remote IP is known and the socket is connected.
+
+
+
+Function calls for HTTP CCC
+---------------------------
+
+ a_Capi_open_url
+ if (reload)
+ Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
+ Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
+ Http_get
+ a_Cache_open_url
+ if URL_E2EReload -> prepare reload
+ if cached
+ client enqueue
+ delayed process queue
+ else
+ Cache_entry_add
+ client enqueue
+
+ -//->
+ a_Http_dns_cb
+ Http_connect_socket
+ OpSend FD, BCK
+ OpSend FD, FWD
+ Http_send_query
+ a_Http_make_query_str
+ OpSend, BCK
+ IO_submit
+ a_IOwatch_add_fd (DIO_WRITE, ...)
+
+
+ Note about 'web' structures. They're created using a_Web_new().
+The web.c module keeps a list of valid webs, so anytime you're
+unsure of a weak reference to 'web', it can be checked with
+a_Web_valid(web).
+
+
+
+------------
+Dpi protocol
+------------
+
+
+ Query: |
+ .
+ 1B --> 1B 1B --> 1B --> | -------------.
+ .----. .----. .----. . |
+I |Capi| |Dpi | | IO | | |
+ '----' '----' '----' . |
+ 1F <-- 1F 1F <-- 1F | V
+ .
+ | [Server]
+ .
+ Answer (same as HTTP): | |
+ . |
+ 2B --> 2B 2B --> 2B | |
+ .----. .----. .----. . |
+II |Capi| |Dpi | | IO | | |
+ '----' '----' '----' . |
+ 2F <-- 2F 2F <-- 2F <-- | <------------'
+ .
+ |
+
+
+CCC Construction:
+
+ a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL
+belongs to a dpi and it is not cached.
+
+ a_Capi_dpi_send_cmd() builds both the Answer and Query chains
+at once (Answer first then Query), in the same way as HTTP does.
+Note that the answer chain is the same for both, and the query
+chain only differs in the module in the middle ([http] or [dpi]).
+
+
+Function calls for DPI CCC
+--------------------------
+
+ a_Capi_open_url
+ a_Capi_dpi_send_cmd
+ Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
+ Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
+ a_Cache_open_url
+ [...]
+
+
+Normal termination:
+
+ When the dpi server is done, it closes the FD, and OpEnd flows
+from IO to Capi (answer branch). When in Capi, capi propagates
+OpEnd to the query branch.
+
+Abnormal termination:
+
+ The transfer may be aborted by a_Capi_conn_abort_by_url(). The
+OpAbort is not yet standardized and has an ad-hoc implementation.
+One idea is to have OpAbort always propagate BCK and then FWD and
+to jump into the other chain when it gets to [Capi].
+
+
+Debugging CCC
+-------------
+
+ A simple way to "look" inside it, is to "#define VERBOSE 1" in
+chain.c, and then to follow its work with a printed copy of the
+diagrams in this document.
+
+ Each new data request generates a CCC, so if you want to debug,
+it's good to refine the testcase to the minimum possible number
+of connections.
+
diff --git a/doc/Cache.txt b/doc/Cache.txt
index ac1ecf87..4e885df2 100644
--- a/doc/Cache.txt
+++ b/doc/Cache.txt
@@ -1,5 +1,5 @@
June 2000, --Jcid
- Last update: Oct 2004
+ Last update: Jul 09
-------
CACHE
@@ -12,28 +12,21 @@ rendering and networking.
calls the cache or the dpi routines depending on the type of
request.
- Every URL must be requested using a_Capi_open_url, no matter
-if it is a http, file, dpi or whatever type of request. The capi
-asks the dpi module for dpi URLs and the Cache for everything
-else.
+ Every URL must be requested using a_Capi_open_url, which
+sends the request to the cache if the data is cached, to dillo's
+http module for http: URLs, and through dillo's DPI system for
+other URLs.
Here we'll document non dpi requests.
- The cache, at its turn, sends the requested-data from memory
-(if cached), or opens a new network connection (if not cached).
-
- This means that no mattering whether the answer comes from
-memory or the net, the client requests it through the capi
-wrapper, in a single uniform way.
-
----------------
CACHE PHILOSOPHY
----------------
- Dillo's cache is very simple, every single resource that's
-retrieved (URL) is kept in memory. NOTHING is saved. This is
-mainly for three reasons:
+ Dillo's cache is very simple; every single resource that's
+retrieved (URL) is kept in memory. NOTHING is saved to disk.
+This is mainly for three reasons:
- Dillo encourages personal privacy and it assures there'll be
no recorded tracks of the sites you visited.
@@ -42,7 +35,7 @@ no recorded tracks of the sites you visited.
serve as caches.
- If you still want to have cached stuff, you can install an
-external cache server (as WWWOFFLE), and benefit from it.
+external cache server (such as WWWOFFLE), and benefit from it.
---------------
@@ -51,15 +44,14 @@ external cache server (as WWWOFFLE), and benefit from it.
Currently, dillo's cache code is spread in different sources:
mainly in cache.[ch], dicache.[ch] and it uses some other
-functions from mime.c, Url.c and web.c.
+functions from mime.c and web.cc.
- Cache.c is the principal source, and it also is the main
+ Cache.c is the principal source, and it also is the one
responsible for processing cache-clients (held in a queue).
-Dicache.c is the "decompressed image cache" and it holds the
-original data and its corresponding decompressed RGB
-representation (more on this subject in Images.txt).
+Dicache.c is the interface to the decompressed RGB representations
+of currently-displayed images held in DW's imgbuf.
- Url.c, mime.c and web.c are used for secondary tasks; as
+ mime.c and web.cc are used for secondary tasks such as
assigning the right "viewer" or "decoder" for a given URL.
@@ -67,7 +59,7 @@ assigning the right "viewer" or "decoder" for a given URL.
A bit of history
----------------
- Some time ago, the cache functions, URL retrieving and
+ Some time ago, the cache functions, URL retrieval and
external protocols were a whole mess of mixed code, and it was
getting REALLY hard to fix, improve or extend the functionality.
The main idea of this "layering" is to make code-portions as
@@ -76,32 +68,34 @@ improved or replaced without affecting the rest of the browser.
An interesting part of the process is that, as resources are
retrieved, the client (dillo in this case) doesn't know the
-Content-Type of the resource at request-time. It only gets known
-when the resource header is retrieved (think of http), and it
-happens when the cache has the control so, the cache sets the
-proper viewer for it! (unless the Callback function is specified
-with the URL request).
+Content-Type of the resource at request-time. It only becomes known
+when the resource header is retrieved (think of http). This
+happens when the cache has control, so the cache sets the
+proper viewer for it (unless the Callback function was already
+specified with the URL request).
You'll find a good example in http.c.
- Note: Files don't have a header, but the file handler inside
-dillo tries to determine the Content-Type and sends it back in
-HTTP form!
+ Note: All resources received by the cache have HTTP-style headers.
+ The file/data/ftp DPIs generate these headers when sending their
+ non-HTTP resources. Most importantly, a Content-Type header is
+ generated based on file extension or file contents.
-------------
Cache clients
-------------
- Cache clients MUST use a_Cache_open_url to request an URL. The
+ Cache clients MUST use a_Capi_open_url to request an URL. The
client structure and the callback-function prototype are defined,
in cache.h, as follows:
struct _CacheClient {
- gint Key; /* Primary Key for this client */
- const char *Url; /* Pointer to a cache entry Url */
- guchar *Buf; /* Pointer to cache-data */
- guint BufSize; /* Valid size of cache-data */
+ int Key; /* Primary Key for this client */
+ const DilloUrl *Url; /* Pointer to a cache entry Url */
+ int Version; /* Dicache version of this Url (0 if not used) */
+ void *Buf; /* Pointer to cache-data */
+ uint_t BufSize; /* Valid size of cache-data */
CA_Callback_t Callback; /* Client function */
void *CbData; /* Client function data */
void *Web; /* Pointer to the Web structure of our client */
@@ -124,28 +118,15 @@ Key-functions descriptions
--------------------------
································································
-int a_Cache_open_url(const char *Url, CA_Callback_t Call, void *CbData)
+int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData)
- if Url is not cached
+ if Web->url is not cached
Create a cache-entry for that URL
Send client to cache queue
- Initiate a new connection
else
Feed our client with cached data
································································
-ChainFunction_t a_Url_get_ccc_funct(const char *Url)
-
- Scan the Url handlers for a handler that matches
- If found
- Return the CCC function for it
- else
- Return NULL
-
- * Ex: If Url is an http request, a_Http_ccc is the matching
-handler.
-
-································································
----------------------
Redirections mechanism
@@ -177,9 +158,9 @@ Notes
to document it in more detail later (source is commented).
Currently I have a drawing to understand it; hope the ASCII
translation serves the same as the original.
- If you're planning to understand the cache process troughly,
-write me a note, just to assign a higher priority on further
-improving of this doc.
+ If you're planning to understand the cache process thoroughly,
+write me a note and I will assign higher priority to further
+improvement of this doc.
Hope this helps!
diff --git a/doc/Cookies.txt b/doc/Cookies.txt
index c43cacc4..a8e82a83 100644
--- a/doc/Cookies.txt
+++ b/doc/Cookies.txt
@@ -1,21 +1,23 @@
Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com,
Jorge Arellano Cid --
-Last update: October 2008
+Last update: March 2010
==================
Cookies in Dillo
==================
-Supported: old Netscape style, RFC 2109, RFC 2965.
+Dillo's cookies implementation is guided by ongoing work by the HTTP State WG
+( http://www.ietf.org/dyn/wg/charter/httpstate-charter ) to specify current
+real-world cookies usage.
Cookies are handled by a dpi (plugin) which shares them between your
instances of Dillo.
+Currently, cookie limits are: 20 per domain, and 1200 in total.
+
When the dpi exits, cookies that you have ACCEPTed are saved to
~/.dillo/cookies.txt, and ACCEPT_SESSION cookies are forgotten.
-(Currently the only limit to the amount of cookies to save to disk is
-a maximum of 20 per domain.)
The dpi normally exits after a period of inactivity, but you can force it to
exit with the command "dpidc stop".
@@ -30,12 +32,14 @@ Out of the box, dillo rejects all cookies.
If you want to accept certain cookies, you can specify rules for different
domains in the file ~/.dillo/cookiesrc. The syntax looks like:
+#host action
DEFAULT DENY
-slashdot.org ACCEPT
+fltk.org ACCEPT
.host.com ACCEPT_SESSION
+Line 0: Comment line begins with '#'.
Line 1: Deny all cookies from all domains not otherwise specified.
-Line 2: Accept all cookies from slashdot.org, and save them to
+Line 2: Accept all cookies from fltk.org, and save them to
~/.dillo/cookies.txt when the cookies dpi exits.
Line 3: Accept all cookies from all subdomains of host.com, but
do not save them when the dpi exits.
diff --git a/doc/Dillo.txt b/doc/Dillo.txt
index 47f89780..b9ef0513 100644
--- a/doc/Dillo.txt
+++ b/doc/Dillo.txt
@@ -24,30 +24,30 @@ developers.
Dillo can be viewed as the sum of five main parts:
1.- Dillo Widget: A custom widget, FLTK2 based, that holds the
-neccesary data structures and mechanisms for graphical rendering.
+necessary data structures and mechanisms for graphical rendering.
(Described in Dw*.txt, dw*.c files among the sources.)
2.- Dillo Cache: Integrated with a signal driven Input/Output
engine that handles file descriptor activity, the cache acts as
the main abstraction layer between rendering and networking.
Every URL, whether cached or not, must be retrieved using
-a_Cache_open_url (Described briefly in Cache.txt, source
-contained in cache.c).
- IO is described in IO.txt (recommended), source in IO/.
+a_Capi_open_url (Described briefly in Cache.txt, source
+contained in capi.c).
+ IO is described in IO.txt (recommended), source in src/IO/.
3.- The HTML parser: A streamed parser that joins the Dillo
Widget and the Cache functionality to make browsing possible
-(Described in HtmlParser.txt, source mainly inside html.c).
+(Described in HtmlParser.txt, source mainly inside html.cc).
4.- Image processing code: The part that handles image
-retrieving, decoding, caching and displaying. (Described in
-Images.txt. Sources: image.c, dw_image.c, dicache.c, gif.c,
+retrieval, decoding, caching and displaying. (Described in
+Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c,
jpeg.c and png.c)
5.- The dpi framework: a gateway to interface the browser with
external programs (Example: the bookmarks server plugin).
Dpi spec: http://www.dillo.org/dpi1.html
-
+
-------------------------
HOW IS THE PAGE RENDERED?
@@ -55,34 +55,27 @@ Dpi spec: http://www.dillo.org/dpi1.html
(A short description of the internal function calling process)
- When the user requests a new URL, a_Interface_entry_open_url
+ When the user requests a new URL, a_UIcmd_open_url
is queried to do the job; it calls a_Nav_push (The highest level
URL dispatcher); a_Nav_push updates current browsing history and
calls Nav_open_url. Nav_open_url closes all open connections by
-calling a_Interface_stop and a_Interface_stop, and then calls
-a_Capi_open_url wich calls a_Cache_open_url (or the dpi module if
+calling a_Bw_stop_clients, and then calls
+a_Capi_open_url which calls a_Cache_open_url (or the dpi module if
this gateway is used).
- If Cache_search hits (due to a cached url :), the client is
+ If Cache_entry_search hits (due to a cached url :), the client is
fed with cached data, but if the URL isn't cached yet, a new CCC
-(Concomitant Control Chain) is created and commited to fetch the
-URL. Note that a_Cache_open_url will return the requested URL,
-whether cached or not.
+(Concomitant Control Chain) is created and committed to fetch the
+URL.
The next CCC link is dynamically assigned by examining the
-URL's protocol. It can be:
-
- a_Http_ccc
- a_File_ccc
- a_About_ccc
- a_Plugin_ccc (not implemented yet)
-
+URL's protocol. It can be a_Http_ccc or a_Dpi_ccc.
- If we have a HTTP URL, a_Http_ccc will succeed, and the http
+ If we have an HTTP URL, a_Http_ccc will succeed, and the http
module will be linked; it will create the proper HTTP query and
link the IO module to submit and deliver the answer.
- Note that as the Content-type of the URL is not always known
+ Note that as the Content-Type of the URL is not always known
in advance, the answering branch decides where to dispatch it to
upon HTTP header arrival.
@@ -94,7 +87,7 @@ called for each tag (to parse and call the appropriate methods)
and the whole page is contructed in a streamed way.
Somewhere in the middle of it, resize and repaint functions
are activated and idle functions draw to screen what has been
-processed.
+processed.
(The process for images is described in Images.txt)
diff --git a/doc/Dpid.txt b/doc/Dpid.txt
index 8f69843e..bf9e8222 100644
--- a/doc/Dpid.txt
+++ b/doc/Dpid.txt
@@ -1,6 +1,6 @@
Aug 2003, Jorge Arellano Cid,
Ferdi Franceschini --
-Last update: Dec 2004
+Last update: Nov 2009
------
@@ -14,7 +14,7 @@ Nomenclature:
dpi:
generic term referring to dillo's plugin system (version1).
- dpi1:
+ dpi1:
specific term for dillo's plugin spec version 1.
at: http://www.dillo.org/dpi1.html
@@ -39,27 +39,24 @@ Nomenclature:
never run more than one instance of a server plugin at a time.
filter plugin:
- Any program/script that can read or write to stdio. If you can write a
- shell script you can write one of these (see examples at the end).
+ A dpi program that reads from stdin and writes to stdout, and that
+ exits after its task is done (they don't remain as server plugins).
Warning, dpid will run multiple instances of filter plugins if requested.
- This is safe if the plugin only writes to stdout which is what the filter
- type dpis do at the moment.
-----------
About dpid:
-----------
* dpid is a program which manages dpi connections.
- * dpid is a daemon that serves dillo using unix domain
- sockets (UDS).
+ * dpid is a daemon that serves dillo using IDS sockets.
* dpid launches dpi programs and arranges socket communication
between the dpi program and dillo.
The concept and motivation is similar to that of inetd. The
-plugin manager (dpid) listens for a service request on a Unix
-domain socket and returns the socket name of a plugin that
-handles the service. It also watches sockets of inactive plugins
-and starts them when a connection is requested.
+plugin manager (dpid) listens for a service request on a socket
+and returns the socket/port pair of a plugin that handles the
+service. It also watches sockets of inactive plugins and starts
+them when a connection is requested.
-----------------------------------------------------------
@@ -72,9 +69,9 @@ problems (briefly outlined here):
* When having two or more running instances of Dillo, one
should prevail, and take control of dpi managing (but all
dillos carry the managing code).
- * If the managing dillo exits, it must pass control to another
+ * If the managing-dillo exits, it must pass control to another
instance, or leave it void if there's no other dillo running!
- * The need to synchronise all the running instances of
+ * The need to synchronize all the running instances of
dillo arises.
* If the controlling instance finishes and quits, all the
dpi-program PIDs are lost.
@@ -83,7 +80,7 @@ problems (briefly outlined here):
* Forks can be expensive (Dillo had to fork its dpis).
* When a managing dillo exits, the new one is no longer the
parent of the forked dpis.
- * If the Unix domain sockets for the dpis were to be named
+ * If Unix domain sockets for the dpis were to be named
randomly, it gets very hard to recover their names if the
controlling instance of dillo exits and another must "take
over" the managing.
@@ -109,12 +106,12 @@ What does dpid handle?
* Different implementations of the same service
dpi programs ("dpis") are just an implementation of a
- service. There's no problem in having more than one for the
- same service.
+ service. There's no problem in implementing a different one
+ for the same service (e.g. downloads).
* Upgrading a service:
to a new version or implementation without requiring
- bringing down the dpid or patching dillo's core.
+ patching dillo's core or even bringing down the dpid.
And finally, being aware that this design can support the
@@ -124,8 +121,8 @@ following functions is very helpful:
------------------------------------------------------------
* "one demand/one response" man, preferences, ...
* "resident while working" downloads, mp3, ...
- * "resident until TERM signal" bookmarks, ...
-
+ * "resident until exit request" bookmarks, ...
+
* "one client only" cd burner, ...
* "one client per instance" man, ...
* "multiple clients/one instance" downloads, cookies ...
@@ -136,12 +133,12 @@ Features
--------
* Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
- "hello.filter.dpi". The ".filter" plugins simply read and write to stdio
- and can be implemented with a shell script easily.
+ "hello.filter.dpi". The ".filter" plugins simply read from stdin
+ and write to stdout.
* Register/update/remove dpis from list of available dpis when a
- <dpi cmd='register_all'> is received.
- * dpid terminates when it receives a <dpi cmd='DpiBye'> command.
- * dpis can be terminated with a <dpi cmd='DpiBye'> command.
+ 'register_all' command is received.
+ * dpid terminates when it receives a 'DpiBye' command.
+ * dpis can be terminated with a 'DpiBye' command.
* dpidc control program for dpid, currently allows register and stop.
@@ -151,36 +148,7 @@ todo:
These features are already designed, waiting for implementation:
- * How to register/update/remove/ individual dpis?
- * How to kill dpis? (signals)
-
- How:
-
- A useful and flexible way is to have a "control program" for
-dpid (it avoids having to find its PID among others).
-
- Let's say:
-
- dpidc [register | upgrade | stop | ...]
-
- It can talk to a dpid UDS that serves for that (the same that
-dillo would use). That way we may also have a dpidc dpi! :-)
-
- Seriously, what I like from this approach is that it is very
-flexible and can be implemented incrementally ("dpidc register"
-is enough to start).
-
- It also avoids the burden of having to periodically check the
-dpis directory structure for changes).
-
- It also lets shell scripts an easy way to do the "dirty" work
-of installing dpis; as is required with distros' package
-systems.
-
-<note>
- How do we tell a crashed dpi? That's the question.
- We're thinking about using the "lease" concept (as in JINI).
-</note>
+ * dpidc remove // May be not necessary after all...
-----------------
@@ -193,14 +161,11 @@ o on startup dpid reads dpidrc for the path to the dpi directory
o both directories are scanned for the list of available plugins.
~/.dillo/dpi overrides system-wide dpis.
-o ~/.dillo/dpi_socket_dir is then checked for the name of the dpi socket
- directory, if dpi_socket_dir does not exist it will be created.
-
-o next it creates Unix domain sockets for the available plugins and
- then listens for service requests on its own socket (dpid.srs)
+o next it creates internet domain sockets for the available plugins and
+ then listens for service requests on its own socket,
and for connections to the sockets of inactive plugins.
-o dpid returns the name of a plugin's socket when a client (dillo)
+o dpid returns the port of a plugin's socket when a client (dillo)
requests a service.
o if the requested plugin is a 'server' then
@@ -210,7 +175,7 @@ o if the requested plugin is a 'server' then
o if the requested plugin is a 'filter' then
1) dpid accepts the connection
- 2) duplicates the connection on stdio
+ 2) maps the socket fd to stdin/stdout (with dup2)
3) forks and starts the plugin
4) continues to watch the socket for new connections
@@ -226,7 +191,7 @@ dpi service process diagram
(I)
.--- s1 s2 s3 ... sn
- |
+ |
[dpid] [dillo]
|
'--- srs
@@ -236,7 +201,7 @@ dpi service process diagram
(II)
.--- s1 s2 s3 ... sn
- |
+ |
[dpid] [dillo]
| |
'--- srs ------------------'
@@ -262,7 +227,7 @@ dpi service process diagram
.[dpid] | [dillo]
. | | |
. '--- srs '---------------'
- .
+ .
.............[dpi program]
when s3 has activity (incoming data), dpid forks the dpi
@@ -271,7 +236,7 @@ dpi service process diagram
(V)
.--- s1 s2 (s3) ... sn
- |
+ |
[dpid] [dillo]
| |
'--- srs .---------------'
@@ -285,170 +250,80 @@ communication process takes place until the task is done. When
the dpi program exits, dpid resumes listening on the socket (s3).
------------------------------------------------
-How are the unix-domain-sockets for dpis named?
------------------------------------------------
-
- Let's say we have two users, "fred" and "joe".
-
- When Fred's dillo starts its dpid, the dpid creates the
-following directory (rwx------):
-
- /tmp/fred-XXXXXX
-
- using mkdtemp().
-
- and saves that filename within "~/.dillo/dpi_socket_dir".
-
- That way, another dillo instance of user Fred can easily find
-the running dpid's service request socket at:
-
- /tmp/fred-XXXXXX/dpid.srs
-
- (because it is saved in "~/.dillo/dpi_socket_dir").
-
- Now, we have a dpi directory per user, and its permissions are
-locked so only the user has access, thus the following directory
-tree structure should pose no problems:
-
- /tmp/fred-XXXXXX/bookmarks
- /downloads
- /cookies
- /ftp
- ...
- dpid.srs
-
- If user Joe starts his dillo, the same happens for him:
-
- /tmp/joe-XXXXXX/bookmarks
- /downloads
- /cookies
- /ftp
- ...
- dpid.srs
-
-
- What should dpid do at start time:
-
- Check if both, ~/.dillo/dpi_socket_dir and its directory, exist
- (it can also check the ownership and permissions).
-
- If (both exist)
- use them!
- else
- delete ~/.dillo/dpi_socket_dir
- create another /tmp/<user>-XXXXXX directory
- save the new directory name into ~/.dillo/dpi_socket_dir
- (we could also add some paranoid level checks)
-
- To some degree, this scheme solves the tmpnam issue, different
-users of dillo at the same time, multiple dillo instances,
-polluting /tmp (cosmetic), and reasonably accounts for an
-eventual dillo or dpid crash.
-
- It has worked very well so far!
-
-
--------------------------------
So, how do I make my own plugin?
--------------------------------
- First, at least, read the "Developing a dillo plugin" section
-of dpi1 spec! :-)
+ Maybe the simplest way to get started is to understand a few
+concepts and then to use the hands-on method by using/modifying
+the hello dpi. It's designed as an example to get developers
+started.
- Note that the dpi1 spec may not be absolutely accurate, but the
-main ideas remain.
+ ---------
+ Concepts:
+ ---------
- Once you've got the concepts, contrast them with the drawings
-in this document. Once it all makes sense, start playing with
-hello.dpi, you can run it by starting dillo with
- dillo dpi:/hello/
-or entering
- dpi:/hello/
-as the url. Then try to understand how it works (use the drawings)
-and finally look at its code.
+ * Dillo plugins work by communicating two processes: dillo
+ and the dpi.
+ * The underlying protocol (DPIP) has a uniform API which is
+ powerful enough for both blocking and nonblocking IO, and
+ filter or server dpis.
+ * The simplest example is one-request one-answer (for example
+ dillo asks for a URL and the dpi sends it). You'll find
+ this and more complex examples in hello.c
- Really, the order is not that important, what really matters is
-to do it all.
+ First, you should get familiar with the hello dpi as a user:
- Start modifying hello.dpi, and then some more. When you feel
-like trying new things, review the code of the other plugins for
-ideas.
+ $dillo dpi:/hello/
- The hardest part is to try to modify the dpi framework code
-inside dillo; you have been warned! It already supports a lot of
-functionality, but if you need to do some very custom stuff, try
-extending the "chat" command.
+ Once you've played enough with it, start reading the well
+commented code in hello.c and start making changes!
----------------------------------
-Examples: Simple 'filter' plugins
----------------------------------
+ ---------------
+ Debugging a dpi
+ ---------------
- For a quick and dirty introduction to dpis try the following shell scripts.
+ The simplest way is to add printf() feedback using the MSG*
+macros. You can start the dpid by hand on a terminal to force
+messages to go there.
- #!/bin/sh
+ Sometimes more complex dpis need more than MSG*. In this case
+you can use gdb like this.
- read -d'>' dpip_tag # Read dillo's request
+ 1.- Add an sleep(20) statement just after the dpi starts.
+ 2.- Start dillo and issue a request for your dpi. This will
+ get your dpi started.
+ 3.- Standing in the dpi source directory:
+ ps aux|grep dpi
+ 4.- Take note of the dpi's PID and start gdb, then:
+ (gdb) attach <PID>
+ 5.- Continue from there...
- # Don't forget the empty line after the Content-type
- cat <<EOF
- <dpi cmd='start_send_page' url='dpi:/hi/hi.filter.dpi'>
- Content-type: text
- EOF
+ ------------
+ Final Notes:
+ ------------
- echo Hi
-
- Of course you should use html in a real application (perl makes this easy).
+ 1.- If you already understand the hello dpi and want to try
+something more advanced:
- A more useful example uses the "si" system info viewer:
+ * bookmarks.c is a good example of a blocking server
+ * file.c is an advanced example of a server handling multiple
+ non-blocking connections with select().
- #!/bin/sh
- # si - System Information Viewer
+ 2.- Multiple instances of a filter plugin may be run
+concurrently, this could be a problem if your plugin records data
+in a file, however it is safe if you simply write to stdout.
+Alternatively you could write a 'server' plugin instead as they
+are guaranteed not to run concurrently.
- read -d'>' dpip_tag
-
- # We don't need to send the Content-type because "si --html" does this
- # for us.
- cat <<EOF
- <dpi cmd='start_send_page' url='dpi:/si/si.dpi.filter'>
- EOF
-
- si --html
-
- just make sure that you have si installed or you wont get far.
-
-To try out the examples create two directories for the scripts under your home directory as follows:
- mkdir -p ~/.dillo/dpi/hi
- mkdir -p ~/.dillo/dpi/si
-
-then create the scripts and put them in the dpi service directories so that you end up with
- ~/.dillo/dpi/hi/hi.filter.dpi
- ~/.dillo/dpi/si/si.filter.dpi
-
-Don't forget to make them executable.
-
-If dpid is already running register the new plugins with
- dpidc register
-
-You can now test them by entering
- dpi:/hi/
-or
- dpi:/si/
-as the url. Or simply passing the url to dillo on startup
-
- dillo dpi:/si/
+ 3.- The hardest part is to try to modify the dpi framework code
+inside dillo; you have been warned! It already supports a lot of
+functionality, but if you need to do some very custom stuff, try
+extending the "chat" command, or asking in dillo-dev.
- You can edit the files in place while dpid is running and reload them in
-dillo to see the result, however if you change the file name or add a new
-script you must run 'dpidc register'.
-WARNING
-Multiple instances of a filter plugin may be run concurrently, this could be a
-problem if your plugin records data in a file, however it is safe if you simply
-write to stdout. Alternatively you could write a 'server' plugin instead as
-they are guaranteed not to run concurrently.
- >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
+ >>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<
diff --git a/doc/HtmlParser.txt b/doc/HtmlParser.txt
index ec64164d..2eb8be63 100644
--- a/doc/HtmlParser.txt
+++ b/doc/HtmlParser.txt
@@ -1,5 +1,5 @@
October 2001, --Jcid
- Last update: Dec 2004
+ Last update: Jul 2009
---------------
THE HTML PARSER
@@ -11,7 +11,7 @@ and plain text also. It has parsing 'modes' that define its
behaviour while working:
typedef enum {
- DILLO_HTML_PARSE_MODE_INIT,
+ DILLO_HTML_PARSE_MODE_INIT = 0,
DILLO_HTML_PARSE_MODE_STASH,
DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
DILLO_HTML_PARSE_MODE_BODY,
@@ -22,12 +22,12 @@ behaviour while working:
The parser works upon a token-grained basis, i.e., the data
stream is parsed into tokens and the parser is fed with them. The
-process is simple: whenever the cache has new data, it gets
+process is simple: whenever the cache has new data, it is
passed to Html_write, which groups data into tokens and calls the
-appropriate functions for the token type (TAG, SPACE or WORD).
+appropriate functions for the token type (tag, space, or word).
Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
-doesn't try to split the data stream into tokens anymore, it
+doesn't try to split the data stream into tokens anymore; it
simply collects until the closing tag.
------
@@ -65,44 +65,52 @@ TOKENS
As it's a common mistake for human authors to mistype or
forget one of the quote marks of an attribute value; the
parser solves the problem with a look-ahead technique
- (otherwise the parser could skip significative amounts of
- well written HTML).
+ (otherwise the parser could skip significant amounts of
+ properly-written HTML).
* WORD --> Html_process_word
- A word is anything that doesn't start with SPACE, and that's
+ A word is anything that doesn't start with SPACE, that's
outside of a tag, up to the first SPACE or tag start.
SPACE = ' ' | \n | \r | \t | \f | \v
-----------------
-THE PARSING STACK
+THE PARSING STACK
-----------------
The parsing state of the document is kept in a stack:
- struct _DilloHtml {
+ class DilloHtml {
[...]
- DilloHtmlState *stack;
- gint stack_top; /* Index to the top of the stack [0 based] */
- gint stack_max;
+ lout::misc::SimpleVector<DilloHtmlState> *stack;
[...]
};
struct _DilloHtmlState {
- char *tag;
- DwStyle *style, *table_cell_style;
- DilloHtmlParseMode parse_mode;
- DilloHtmlTableMode table_mode;
- gint list_level;
- gint list_number;
- DwWidget *page, *table;
- gint32 current_bg_color;
- };
+ CssPropertyList *table_cell_props;
+ DilloHtmlParseMode parse_mode;
+ DilloHtmlTableMode table_mode;
+ bool cell_text_align_set;
+ DilloHtmlListMode list_type;
+ int list_number;
+
+ /* TagInfo index for the tag that's being processed */
+ int tag_idx;
+
+ dw::core::Widget *textblock, *table;
+ /* This is used to align list items (especially in enumerated lists) */
+ dw::core::Widget *ref_list_item;
+
+ /* This is used for list items etc; if it is set to TRUE, breaks
+ have to be "handed over" (see Html_add_indented and
+ Html_eventually_pop_dw). */
+ bool hand_over_break;
+ };
Basically, when a TAG is processed, a new state is pushed into
the 'stack' and its 'style' is set to reflect the desired
diff --git a/doc/IO.txt b/doc/IO.txt
index fb691223..cd62a4f5 100644
--- a/doc/IO.txt
+++ b/doc/IO.txt
@@ -73,7 +73,7 @@ are found in:
Closing the TCP connection:
The four packet-sending closing sequence of the TCP protocol.
-
+
In a WAN context, every single item of this list has an
associated delay that is non deterministic and often measured in
seconds. If we add several connections per browsed page (each one
@@ -96,7 +96,7 @@ The traditional (blocking) approach
packets that belong to other connections may be arriving,
and have to wait for service.
- Web browsers handle many small transactions,
+ Web browsers handle many small transactions,
if waiting times are not overlapped
the latency perceived by the user can be very annoying.
@@ -219,7 +219,7 @@ arrives, a call-back function is called, and the program resumes
what it was doing at DNS-request time. The interesting thing is
that the call-back happens in the main thread, while the child
thread simply exits when done. This is implemented through a
-server-channel design.
+server-channel design.
The server channel
@@ -252,7 +252,7 @@ Basically, a socket file descriptor of AF_INET type is requested
and set to non-blocking I/O. When the DNS server has resolved the
name, the socket connection process begins by calling connect(2);
{We use the Unix convention of identifying the manual section
- where the concept is described, in this case
+ where the concept is described, in this case
section 2 (system calls).}
which returns immediately with an EINPROGRESS error.
@@ -311,7 +311,7 @@ an error condition).
----------------------
Closing the connection
----------------------
-
+
Closing a TCP connection requires four data segments, not an
impressive amount but twice the round trip time, which can be
substantial. When data retrieval finishes, socket closing is
@@ -370,7 +370,7 @@ passed to the I/O engine.
'ExtData' MAY be provided.
'Status', 'FD' and 'GioCh' are set by I/O engine internal
-routines.
+routines.
When there is new data in the file descriptor, 'IO_callback'
gets called (by glib). Only after the I/O engine finishes
@@ -383,7 +383,7 @@ The I/O engine transfer buffer
The 'Buf' and 'BufSize' fields of the request structure
provide the transfer buffer for each operation. This buffer must
be set by the client (to increase performance by avoiding copying
-data).
+data).
On reads, the client specifies the amount and where to place
the retrieved data; on writes, it specifies the amount and source
diff --git a/doc/Images.txt b/doc/Images.txt
index 96e09579..6a36e6f5 100644
--- a/doc/Images.txt
+++ b/doc/Images.txt
@@ -1,4 +1,4 @@
- December 2007, --Jcid
+ January 2009, --Jcid
------
IMAGES
@@ -27,22 +27,24 @@ passed to the widget (DwImage) and drawn in a streamed way.
Note that INDEXED images are also decoded into RGB format.
Html_tag_open_img // IMG element processing
- Html_add_new_image // Reat attributes, create image, add to HTML page
- Html_load_image // Tells cache to retrieve image
+ Html_add_new_image // Read attributes, create image, add to HTML page
a_Image_new // Create a 'DilloImage' data structure, to coordinate
// decoded image-data transfer to an 'Imgbuf'.
Html_add_widget // Adds the dw::Image to the page
+ Html_load_image // Tells cache to retrieve image
---------------------
Fetching from the net
---------------------
-* a_Cache_open_url initiates the resource request, and when
+* a_Capi_open_url initiates the resource request, and when
finally the answer arrives, the HTTP header is examined for MIME
type and either the GIF or PNG or JPEG decoder is set to handle
the incoming data stream.
- Decoding functions: a_Gif_image, a_Jpeg_image and a_Png_image.
+
+ Decoding functions:
+ a_Gif_callback, a_Jpeg_callback and a_Png_callback.
* The decoding function calls the following dicache methods as
the data is processed (listed in order):
@@ -50,58 +52,62 @@ the data is processed (listed in order):
a_Dicache_set_parms
a_Dicache_set_cmap (only for indexed-GIF images)
a_Dicache_write
+ a_Dicache_new_scan (MAY be called here or after set_cmap)
a_Dicache_close
+
* The dicache methods call the necessary functions to connect
with the widget code. This is done by calling image.c functions:
a_Image_set_parms
a_Image_set_cmap
a_Image_write
+ a_Image_new_scan
a_Image_close
-
-* The functions in image.c make the required a_Dw_image_...
-calls.
+
+* The functions in image.c make the required Dw calls.
-------------------------
Fetching from the dicache
-------------------------
-* a_Cache_open_url tests the cache for the image, and directly
-enqueues a cache client for it, without asking the network for
-the data. When the client queue is processed (a bit later), the
-decoder is selected based on the cache entry Type.
+* a_Capi_open_url() tests the cache for the image, and the cache,
+via a_Cache_open_url(), enqueues a client for it, without asking
+the network for the data. When the client queue is processed (a
+bit later), Dicache_image() is set as the callback.
-* When the decoder is called, it tests the dicache for the image;
-if the image is found, then it sets a_Dicache_callback as the
-handling function and gets out of the way (no image decoding is
-needed).
+* When Dicache_image() is called, it sets the proper image data
+decoder (RGB) and its data structure based on the entry's Type.
+Then it substitutes itself with a_Dicache_callback() as the
+handling function, and gets out of the way.
-* Later on, the DwImage buffer is set to reference the
-dicache-entry's buffer and the rest of the functions calls is
-driven by a_Dicache_callback.
+* Thenceforth the rest of the functions calls is driven by
+a_Dicache_callback().
-----------
Misc. notes
-----------
-* Repeated images generate new cache clients, but only one of
-them (the first) is handled with a_Dicache_* functions, the rest
-is done with a_Dicache_callback..
+* Repeated images generate new cache clients, but they may share
+the imgbuf.
+ Note: Currently there's no proper support for transparent
+images (i.e. decode to RGBA), but most of the time they render
+the background color OK. This is: when first loaded, repeated
+images share a background color, but when cached they render
+correctly ;-). There's no point in trying to fix this because the
+correct solution is to decode to RGBA and let the toolkit (FLTK)
+handle the transparency.
-* The cache-client callback is set when the Content-type of the
-image is got. It can be: a_Png_image, a_Gif_image or a_Jpeg_image
-Those are called 'decoders' because their main function is to
-translate the original data into RGB format.
+* The first cache-client callback (Dicache_image()) is set when
+the Content-type of the image is got.
-* Later on, the decoder can substitute itself, if it finds the
-image has been already decoded, with a_Dicache_callback function.
-This avoids decoding it twice.
+* Later on, when there's a shared imgbuf, the dicache's logic
+avoids decoding it multiple times and reuses what's already done.
* The dicache-entry and the Image structure hold bit arrays that
-represent which rows had been decoded.
+represent which rows have been decoded.
* The image processing can be found in the following sources:
@@ -111,7 +117,7 @@ represent which rows had been decoded.
- dw/image.{cc,hh}
* Bear in mind that there are four data structures for image
-code:
+code:
- DilloImage (image.hh)
- DICacheEntry (dicache.h)
diff --git a/doc/Imgbuf.txt b/doc/Imgbuf.txt
index 1039ff9e..f4a56660 100644
--- a/doc/Imgbuf.txt
+++ b/doc/Imgbuf.txt
@@ -135,9 +135,9 @@ What happens in different situations:
scaler is not yet in the list, it is added, and a local scaler is
created.
-
- -
+
+ -
There are three images in the page, i1a, i1b, and i2. I1a and i1b
refer to the same image recource, represented by the root image buffer
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 9000fec0..d644393b 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,4 +1,7 @@
+dist_doc_DATA = user_help.html
+man_MANS = dillo.1
EXTRA_DIST = \
+ $(man_MANS) \
index.doc \
lout.doc \
dw-map.doc \
diff --git a/doc/NC_design.txt b/doc/NC_design.txt
index 6932b9cf..380787f6 100644
--- a/doc/NC_design.txt
+++ b/doc/NC_design.txt
@@ -1,13 +1,13 @@
_________________________________________________________________
-
+
Naming&Coding design
_________________________________________________________________
-
+
Dillo's code is divided into modules. For instance: bookmark, cache,
dicache, gif.
-
- Lets think of a module named "menu", then:
+
+ Let's think of a module named "menu", then:
* Every internal routine of the module, should start with "Menu_"
prefix.
* "Menu_" prefixed functions are not meant to be called from outside
@@ -15,66 +15,113 @@
* If the function is to be exported to other modules (i.e. it will
be called from the outside), it should be wrapped with an "a_"
prefix.
-
+
For instance: if the function name is "Menu_create", then it's an
internal function, but if we need to call it from the outside, then it
should be renamed to "a_Menu_create".
-
+
Why the "a_" prefix?
Because of historical reasons.
- And it reads better "a_Menu_create" than "d_Menu_create" cause the
+ And "a_Menu_create" reads better than "d_Menu_create" because the
first one suggests "a Menu create" function!
-
+
Another way of understanding this is thinking of "a_" prefixed
functions as Dillo's internal library, and the rest ("Menu_" prefixed
in our example) as a private module-library.
-
- Indentation: Source code must be indented with 3 blank spaces, no Tabs.
+
+ Indentation:
+
+ Source code must be indented with 3 blank spaces, no Tabs.
Why?
Because different editors expand or treat tabs in several ways; 8
spaces being the most common, but that makes code really wide and
we'll try to keep it within the 80 columns bounds (printer friendly).
-
+
+ You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c
+
Function commenting:
-
+
Every single function of the module should start with a short comment
- that explains it's purpose; three lines must be enough, but if you
+ that explains its purpose; three lines must be enough, but if you
think it requires more, enlarge it.
-/*
- * Try finding the url in the cache. If it hits, send the contents
- * to the caller. If it misses, set up a new connection.
- */
-int a_Cache_open_url(const char *url, void *Data)
-{
- ...
- ...
- ...
-}
-
- We also have the BUG: and todo: tags.
+ /*
+ * Try finding the url in the cache. If it hits, send the contents
+ * to the caller. If it misses, set up a new connection.
+ */
+ int a_Cache_open_url(const char *url, void *Data)
+ {
+ ...
+ ...
+ ...
+ }
+
+ We also have the BUG:, TODO:, and WORKAROUND: tags.
Use them within source code comments to spot hidden issues. For
instance:
-/* BUG: this counter is not accurate */
-++i;
+ /* BUG: this counter is not accurate */
+ ++i;
+
+ /* TODO: get color from the right place */
+ a = color;
+
+ /* WORKAROUND: the canonical way of doing it doesn't work yet. */
+ ++a; ++a; ++a;
+
+ Function length:
+
+ Let's try to keep functions within the 45 lines boundary. This eases
+ code reading, following, understanding and maintenance.
+
+ Functions with a single exit:
+
+ It's much easier to follow and maintain functions with a single exit
+ point at the bottom (instead of multiple returns). The exception to
+ the rule are calls like dReturn_if_fail() at its head.
+
+ dlib functions:
+
+ * Dillo uses dlib extensively in its C sources. Before starting
+ to code something new, a good starting point is to check what
+ this library has to offer (check dlib/dlib.h).
+ * Memory management must be done using dNew, dNew0, dMalloc, dFree
+ and their relatives.
+ * For debugging purposes and error catching (not for normal flow):
+ dReturn_if_fail, dReturn_val_if_fail etc. are encouraged.
+ * The MSG macro is extensively used to output additional information
+ to the calling terminal.
+
+ _________________________________________________________________
+
+ C++
+
+ Source code in C++ should follow the same rules with these exceptions:
+
+ * Class method names are camel-cased and start with lowercase
+ e.g. appendInputMultipart
+ * Classes and types start uppercased
+ e.g. class DilloHtmlReceiver
+ * Class methods don't need to prefix its module name
+ e.g. links->get()
+
+ We also try to keep the C++ relatively simple. Dillo does use
+ inheritance and templates, but that's about all.
-/* todo: get color from the right place */
-a = color;
_________________________________________________________________
-
+
What do we get with this?
-
+
* A clear module API for Dillo; every function prefixed "a_" is to
be used outside the module.
- * A way for identifying where the function came from (the
+ * A way to identify where the function came from (the
capitalized word is the module name).
- * An inner ADT (Abstract data type) for the module. That can be
+ * An inner ADT (Abstract data type) for the module that can be
isolated, tested and replaced independently.
* A two stage instance for bug-fixing. You can change the exported
function algorithms while someone else fixes the internal
module-ADT!
* A coding standard ;)
_________________________________________________________________
-
+
Naming&Coding design by Jorge Arellano Cid
diff --git a/doc/README b/doc/README
index b3fbd136..8b0a8d63 100644
--- a/doc/README
+++ b/doc/README
@@ -1,15 +1,21 @@
-README: Last update Oct 2008 --jcid
+README: Last update Jul 2009
- This documents need a review. They were current with dillo1 and
-now, with dillo2, most of them are obsolete, specially Dw*txt, but
-Dw2 is fully documented in html using doxygen.
+These documents cover dillo's internals.
+For user help, see http://www.dillo.org/dillo2-help.html
- The other documents will be reviewed when I have some time. They
-will give you an overview of what's going on but take them with a
-pinch of salt.
+--------------------------------------------------------------------------
- Of course I'd like to have these as doxygen files too!
-If somebody wants to make this convertion, please let me know
+These documents need a review.
+*.txt were current with Dillo1, but many have since become more or
+ less out-of-date.
+*.doc are doxygen source for the Dillo Widget (dw) component, and
+ were written for Dillo2.
+
+They will give you an overview of what's going on, but take them
+with a pinch of salt.
+
+ Of course I'd like to have *.txt as doxygen files too!
+If somebody wants to make this conversion, please let me know
to assign higher priority to updating these docs.
--
@@ -31,16 +37,14 @@ Jorge.-
Cookies.txt Explains how to enable cookies Current
Dpid.txt Dillo plugin daemon Current
--------------------------------------------------------------------------
- [This documents cover dillo's internal working. They're NOT a user manual]
- --------------------------------------------------------------------------
- * Ah!, there's a small program (srch) within the src/ dir. It searches
+ * BTW, there's a small program (srch) within the src/ dir. It searches
tokens within the whole code (*.[ch]). It has proven very useful.
Ex: ./srch a_Image_write
./srch todo:
- * Please submit your patches with 'diff -pru'.
+ * Please submit your patches with 'hg diff'.
Happy coding!
diff --git a/doc/Selection.txt b/doc/Selection.txt
index 08fe0abc..7904bd94 100644
--- a/doc/Selection.txt
+++ b/doc/Selection.txt
@@ -26,7 +26,7 @@ The widget must construct simple iterators (DwIterator), which will be
transferred to extended iterators (DwExtIterator), see below for more
details. All event handling functions have the same signature, the
arguments in detail are:
-
+
- DwIterator *it the iterator pointing on the item under
the mouse pointer,
- gint char_pos the exact (character) position within
diff --git a/doc/dillo.1 b/doc/dillo.1
new file mode 100644
index 00000000..6e96cf99
--- /dev/null
+++ b/doc/dillo.1
@@ -0,0 +1,101 @@
+.TH dillo 1 "August 5, 2010" "version 2.2" "USER COMMANDS"
+.SH NAME
+dillo \- web browser
+.SH SYNOPSIS
+.B dillo
+.RI [ OPTION ]...
+.RB [ \-\- ]
+.RI [ URL | FILE ]...
+.SH DESCRIPTION
+.PP
+Dillo is a lightweight graphical web browser that aims to be secure.
+It handles HTTP internally, and FILE, FTP, and
+DATA URIs are handled through a plugin system (dpi). In addition,
+.I INSECURE
+HTTPS support can be enabled. Both FTP and Dillo's download manager use the
+.BR wget (1)
+downloader.
+.PP
+Dillo displays HTML, text, PNG, JPEG, and GIF files.
+It handles cookies, basic authentication, proxying, and some CSS.
+.PP
+Framesets are displayed as links to frames, and there is currently
+no support for javascript or video.
+.SH OPTIONS
+.TP
+\fB\-f\fR, \fB\-\-fullwindow\fR
+Start in full window mode: hide address bar, navigation buttons, menu, and
+status bar.
+.TP
+\fB\-g\fR, \fB\-\-geometry \fIGEO\fR
+Set initial window position where \fIGEO\fR is
+\fIW\fBx\fIH\fR[{\fB+\-\fR}\fIX\fR{\fB+\-\fR}\fIY\fR].
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display this help text and exit.
+.TP
+\fB\-l\fR, \fB\-\-local\fR
+Don't load images for these URL(s).
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+Display version info and exit.
+.TP
+\fB\-x\fR, \fB\-\-xid \fIXID\fR
+Open first Dillo window in an existing window whose window ID is \fIXID\fR.
+.SH EXIT STATUS
+.TP
+.B 0
+No error.
+.TP
+.B 1
+Internal error.
+.TP
+.B 2
+Error in command line arguments.
+.SH ENVIRONMENT
+.TP
+.BR "HOME " "(or " "HOMEDRIVE " "and " "HOMEPATH " "on Cygwin)"
+User's home directory.
+.TP
+.B http_proxy
+URL of proxy to send HTTP traffic through.
+.SH FILES
+.TP
+.I dpid
+Dillo plugin daemon
+.TP
+.I dpidc
+Control program for dpid.
+.TP
+.I ~/.dillo/bm.txt
+User bookmarks
+.TP
+.I ~/.dillo/certs/
+Saved certificates for HTTPS.
+.TP
+.I ~/.dillo/cookies.txt
+Stored cookies
+.TP
+.I ~/.dillo/cookiesrc
+Cookie settings
+.TP
+.I ~/.dillo/dillorc
+Configuration file.
+.TP
+.I ~/.dillo/dpid_comm_keys
+Keys used in dpi daemon communication.
+.TP
+.I ~/.dillo/dpidrc
+Contains name of directory containing dpis, and associates
+dpi files with protocols.
+.TP
+.I ~/.dillo/keysrc
+Keybindings.
+.TP
+.I ~/.dillo/style.css
+User style sheet
+.SH SEE ALSO
+.BR wget (1)
+.PP
+Dillo website:
+.B http://www.dillo.org
diff --git a/doc/dw-changes.doc b/doc/dw-changes.doc
index b76e9433..7050df9a 100644
--- a/doc/dw-changes.doc
+++ b/doc/dw-changes.doc
@@ -17,10 +17,6 @@ Related to the FLTK port, there have been many changes, this is a
<li> The central class managing the widget tree is not anymore
GtkDwViewport, but dw::core::Layout.
- <li> There are multiple views (implementations of dw::core::View).
- Typically, you keep a reference on dw::core::Layout and a "main"
- view (a viewport).
-
<li> Drawing is done via dw::core::View, a pointer is passed to
dw::core::Widget::draw.
@@ -91,7 +87,7 @@ The old documentation has been moved to:
<tr><td>remarks on specific widgets <td>respective source files: dw::Bullet,
dw::core::ui::Embed
<tr><td rowspan="2">DwImage.txt
- <td>signals <td>dw::core::Widget::LinkReceiver
+ <td>signals <td>dw::core::Layout::LinkReceiver
<tr><td>rest <td>dw::Image,
\ref dw-images-and-backgrounds
<tr><td colspan="2">Imgbuf.txt <td>dw::core::Imgbuf,
@@ -106,4 +102,4 @@ The old documentation has been moved to:
<tr><td colspan="2">Selection.txt <td>dw::core::SelectionState
</table>
-*/ \ No newline at end of file
+*/
diff --git a/doc/dw-example-screenshot.png b/doc/dw-example-screenshot.png
new file mode 100644
index 00000000..a4d37903
--- /dev/null
+++ b/doc/dw-example-screenshot.png
Binary files differ
diff --git a/doc/dw-images-and-backgrounds.doc b/doc/dw-images-and-backgrounds.doc
index c4991807..bb55aca5 100644
--- a/doc/dw-images-and-backgrounds.doc
+++ b/doc/dw-images-and-backgrounds.doc
@@ -109,7 +109,7 @@ digraph G {
}
"DICache Entry" -> ImageRenderer [headlabel="*", taillabel="1"];
- "DICache Entry" -> Imgbuf [headlabel="1", taillabel="1"];
+ "DICache Entry" -> Imgbuf [headlabel="1", taillabel="1"];
BgRenderer -> Imgbuf [headlabel="1", taillabel="*"];
BgRenderer -> BgAllocation [headlabel="*", taillabel="1"];
diff --git a/doc/dw-layout-views.doc b/doc/dw-layout-views.doc
index f064c671..d1118489 100644
--- a/doc/dw-layout-views.doc
+++ b/doc/dw-layout-views.doc
@@ -4,23 +4,18 @@ Rendering of Dw is done in a way resembling the model-view pattern, at
least formally. Actually, the counterpart of the model, the layout
(dw::core::Layout), does a bit more than a typical model, namely the
layouting (delegated to the widget tree, see \ref dw-layout-widgets),
-and the views do a bit less than a typical view, i.e. only the actual
+and the view does a bit less than a typical view, i.e. only the actual
drawing.
Additionally, there is a structure representing common properties of
-the platform, views generally work only together with one specific
-platform. A platform is typically related to the underlying UI
+the platform. A platform is typically related to the underlying UI
toolkit, but other uses may be thought of.
-This design helps to archieve three important goals:
+This design helps to archieve two important goals:
<ul>
-<li> It makes different views of the same document simple, e.g. the
- normal viewport and the preview window.
-
<li> Abstraction of the actual drawing, by different implementations
- of dw::core::View. Most important, there must be a viewport, but
- some other views are possible, e.g. a preview window.
+ of dw::core::View.
<li> It makes portability simple.
</ul>
@@ -29,7 +24,7 @@ This design helps to archieve three important goals:
<h2>Viewports</h2>
Although the design implies that the usage of viewports should be
-fully transparent to the layout module, this cannot be fully archived,
+fully transparent to the layout module, this cannot be fully achieved,
for the following reasons:
<ul>
@@ -41,15 +36,10 @@ for the following reasons:
the size of a viewport, the text within should be rewrapped.
</ul>
-Therefor, dw::core::Layout keeps track of the viewport size, the
+Therefore, dw::core::Layout keeps track of the viewport size, the
viewport position, and even the thickness of the scrollbars, they are
-relevant, see below for more details. These sizes are always equal in
-all views. However, a given view may not use viewports at all, and
-there may be the case, that no view related to a layout uses
-viewports, in this case, the viewport size is not defined at all.
-
-(The case, that there is no viewport at all, is currently not well
-defined, but this case does not occur currently within dillo.)
+relevant, see below for more details.
+If a viewport is not used, however, the size is not defined.
Whether a given dw::core::View implementation is a viewport or not, is
defined by the return value of dw::core::View::usesViewport. If this
@@ -75,7 +65,7 @@ of the viewport. Views using viewports must
</ol>
Applications of scrolling positions (anchors, test search etc.) are
-handled by the layout, in a way fully transparent to the views.
+handled by the layout, in a way fully transparent to the view.
<h3>Scrollbars</h3>
@@ -86,11 +76,10 @@ respectively. If they are not needed, they are hidden, to save screen
space.
Since scrollbars decrease the usable space of a view, dw::core::Layout
-must know how much space they take. Each view returns, via
+must know how much space they take. The view returns, via
dw::core::View::getHScrollbarThickness and
dw::core::View::getVScrollbarThickness, how thick they will be, when
-visible. The total space difference is then the maximum of all values,
-which the views return.
+visible.
Viewport sizes, which denote the size of the viewport widgets, include
scrollbar thicknesses. When referring to the viewport \em excluding
@@ -100,7 +89,7 @@ the area, which is used to display the canvas.
<h2>Drawing</h2>
A view must implement several drawing methods, which work on the whole
-canvas. If it is neccesary to convert them (e.g. into
+canvas. If it is necessary to convert them (e.g. into
dw::fltk::FltkViewport), this is done in a way fully transparent to
dw::core::Widget and dw::core::Layout, instead, this is done by the
view implementation.
@@ -115,8 +104,8 @@ There exist following situations:
<li> A widget requests a redraw: In this case, the widget will
delegate this to the layout (dw::core::Layout::queueDraw), which
- delegates it to all views (dw::core::View::queueDraw).
- Typically, the views will queue these requests, for efficiency.
+ delegates it to the view (dw::core::View::queueDraw).
+ Typically, the view will queue these requests for efficiency.
<li> A widget requests a resize: This case is described below, in short,
dw::core::View::queueDrawTotal is called for the view.
@@ -160,10 +149,9 @@ flickering.
<h2>Sizes</h2>
-Generally, all views show the same layout, which has a given size
-(canvas size). In the simplest case, views do not have an influence on
-the canvas size, so that they are just told about changes of the
-canvas size, by a call to dw::core::View::setCanvasSize. This happens
+In the simplest case, the view does not have any influence on
+the canvas size, so it is told about changes of the
+canvas size by a call to dw::core::View::setCanvasSize. This happens
in the following situations:
<ul>
@@ -177,28 +165,26 @@ in the following situations:
<h3>Viewports</h3>
-Furthermore, viewport sizes and scrollbar thicknesses are always the
-same. There are two cases, in which the viewport size changes:
+There are two cases where the viewport size changes:
<ul>
<li> As an reaction on a user event, e.g. when the user changes the
- window size. In this case, the affected view delegates this
+ window size. In this case, the view delegates this
change to the layout, by calling
- dw::core::Layout::viewportSizeChanged. All other views are
- told about this, by calling dw::core::Layout::setViewportSize.
+ dw::core::Layout::viewportSizeChanged.
<li> The viewport size may also depend on the visibility of UI
widgets, which depend on the world size, e.g scrollbars,
- generally called "viewport markers". This is described in an own
+ generally called "viewport markers". This is described in a separate
section.
</ul>
After the creation of the layout, the viewport size is undefined. When
-a view is attached to a layout, and this view is already to be able to
-define its viewport size, it may already call
+a view is attached to a layout, and this view can already specify
+its viewport size, it may call
dw::core::Layout::viewportSizeChanged within the implementation of
-dw::core::Layout::setLayout. If not, it may do this, as soon as the
-viewport size gets known.
+dw::core::Layout::setLayout. If not, it may do this as soon as the
+viewport size is known.
Generally, the scrollbars have to be considered. If e.g. an HTML page
is rather small, it looks like this:
@@ -267,4 +253,4 @@ This results in the following rules:
is done anymore.
</ol>
-*/ \ No newline at end of file
+*/
diff --git a/doc/dw-layout-widgets.doc b/doc/dw-layout-widgets.doc
index f5b0ed06..d488efd8 100644
--- a/doc/dw-layout-widgets.doc
+++ b/doc/dw-layout-widgets.doc
@@ -81,7 +81,7 @@ different situations:
<ol>
<li> Adding an anchor is inititiated by a specific widget method, e.g.
dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be
- called,
+ called,
<li> Whenever the position of an anchor is changed,
dw::core::Widget::changeAnchor is called (typically, this is done
@@ -107,14 +107,11 @@ In two cases, a widget has to be drawn:
</ol>
In both cases, drawing is done by the implementation of
-dw::core::Widget::draw. Generally, a widget draws into different views
-(see \ref dw-layout-views), the view to draw into is passed as the
-first argument. In the first case, only the view, which causes the
-expose event, is passed, in the second case, dw::core::Widget::draw is
-called multiple times, once for each view connected to the layout.
+dw::core::Widget::draw, which draws into the view.
+
Each view provides some primitive methods for drawing, most should be
-obvious. Notice that the views do not know anything about dillo
+obvious. Note that the views do not know anything about dillo
widgets, and so coordinates have to be passed as canvas coordinates.
A widget may only draw in its own allocation. If this cannot be
@@ -144,7 +141,7 @@ void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
}
\endcode
-Clipping views are expensive, so they should be avoided, when possible.
+Clipping views are expensive, so they should be avoided when possible.
The second argument to dw::core::Widget::draw is the region, which has
to be drawn. This may (but needs not) be used for optimization.
@@ -169,9 +166,9 @@ dw-widget-sizes.)
<h2>Mouse Events</h2>
-A widget may process mouse events. The views (\ref dw-layout-views)
-pass mouse events to the layout, which then passes them to the
-widgtes. There are two kinds of mouse events:
+A widget may process mouse events. The view (\ref dw-layout-views)
+passes mouse events to the layout, which then passes them to the
+widgets. There are two kinds of mouse events:
<ul>
<li>events returning bool, and
@@ -221,9 +218,9 @@ in which the mouse pointer is now ("new widget").
The following paths are calculated:
<ol>
-<li> the path from the old widget to the nearest common anchestor of the old
+<li> the path from the old widget to the nearest common ancestor of the old
and the new widget, and
-<li> the path from this anchestor to the new widget.
+<li> the path from this ancestor to the new widget.
</ol>
For the widgets along these paths, dw::core::Widget::enterNotifyImpl
@@ -232,7 +229,7 @@ and dw::core::Widget::leaveNotifyImpl are called.
<h3>Signals</h3>
If a caller outside of the widget is interested in these events, he
-can connect a dw::core::Widget::EventReceiver. For those events with a
+can connect a dw::core::Layout::LinkReceiver. For those events with a
boolean return value, the results of the signal emission is regarded,
i.e. the delegation of an event to the parent of the widget can be
stopped by a signal receiver returning true, even if the widget method
@@ -267,4 +264,4 @@ background colors to parts of a widget (as dw::Table does for rows),
it must call dw::core::Widget::setBgColor for the children inside this
part.
-*/ \ No newline at end of file
+*/
diff --git a/doc/dw-overview.doc b/doc/dw-overview.doc
index 2414a5da..8c740635 100644
--- a/doc/dw-overview.doc
+++ b/doc/dw-overview.doc
@@ -108,13 +108,13 @@ A short overview:
<ul>
<li> dw::core::Layout is the central class, it manages the widgets and the
- views, and provides delegation methods for the platform.
+ view, and provides delegation methods for the platform.
<li> The layouting is done by a tree of widgets (details are described in
\ref dw-layout-widgets), also the drawing, which is finally delegated
- to the views.
+ to the view.
-<li> The views (implementations of dw::core::View) provide primitive methods
+<li> The view (implementation of dw::core::View) provides primitive methods
for drawing, but also have an influence on
the canvas size (via size hints). See \ref dw-layout-views for details.
@@ -147,11 +147,11 @@ files:
A complete map can be found at \ref dw-map.
-<ul>
+<ul>
<li> For learning, how to use Dw, read \ref dw-usage and related documents,
dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds.
<li> Advanced topics are described in \ref dw-layout-widgets,
\ref dw-widget-sizes and \ref dw-layout-views.
</ul>
-*/ \ No newline at end of file
+*/
diff --git a/doc/dw-size-of-widget.png b/doc/dw-size-of-widget.png
new file mode 100644
index 00000000..eda93ee1
--- /dev/null
+++ b/doc/dw-size-of-widget.png
Binary files differ
diff --git a/doc/dw-style-box-model.png b/doc/dw-style-box-model.png
new file mode 100644
index 00000000..aa65ecb7
--- /dev/null
+++ b/doc/dw-style-box-model.png
Binary files differ
diff --git a/doc/dw-style-length-absolute.png b/doc/dw-style-length-absolute.png
new file mode 100644
index 00000000..6b4d8389
--- /dev/null
+++ b/doc/dw-style-length-absolute.png
Binary files differ
diff --git a/doc/dw-style-length-percentage.png b/doc/dw-style-length-percentage.png
new file mode 100644
index 00000000..15b36958
--- /dev/null
+++ b/doc/dw-style-length-percentage.png
Binary files differ
diff --git a/doc/dw-style-length-relative.png b/doc/dw-style-length-relative.png
new file mode 100644
index 00000000..d54d99b5
--- /dev/null
+++ b/doc/dw-style-length-relative.png
Binary files differ
diff --git a/doc/dw-textblock-collapsing-spaces-1-1.png b/doc/dw-textblock-collapsing-spaces-1-1.png
new file mode 100644
index 00000000..d528dfb2
--- /dev/null
+++ b/doc/dw-textblock-collapsing-spaces-1-1.png
Binary files differ
diff --git a/doc/dw-textblock-collapsing-spaces-1-2.png b/doc/dw-textblock-collapsing-spaces-1-2.png
new file mode 100644
index 00000000..483e79d1
--- /dev/null
+++ b/doc/dw-textblock-collapsing-spaces-1-2.png
Binary files differ
diff --git a/doc/dw-textblock-collapsing-spaces-2-1.png b/doc/dw-textblock-collapsing-spaces-2-1.png
new file mode 100644
index 00000000..0a03ea80
--- /dev/null
+++ b/doc/dw-textblock-collapsing-spaces-2-1.png
Binary files differ
diff --git a/doc/dw-textblock-collapsing-spaces-2-2.png b/doc/dw-textblock-collapsing-spaces-2-2.png
new file mode 100644
index 00000000..b89c6254
--- /dev/null
+++ b/doc/dw-textblock-collapsing-spaces-2-2.png
Binary files differ
diff --git a/doc/dw-usage.doc b/doc/dw-usage.doc
index d0aa8d36..913a4862 100644
--- a/doc/dw-usage.doc
+++ b/doc/dw-usage.doc
@@ -20,7 +20,7 @@ As described in \ref dw-overview, the following objects are needed:
<li> some widgets (for this example, only a simple dw::Textblock).
</ul>
-First of all, the necessary #include's:
+First of all, the necessary \#include's:
\code
#include <fltk/Window.h>
@@ -39,7 +39,7 @@ int main(int argc, char **argv)
{
\endcode
-As the first object, the platform is instanciated:
+As the first object, the platform is instantiated:
\code
dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
@@ -109,7 +109,7 @@ defined, here dw::core::style::Color::createSimple must be called:
Finally, the style for the widget is created:
\code
- dw::core::style::Style *widgetStyle =
+ dw::core::style::Style *widgetStyle =
dw::core::style::Style::create (layout, &styleAttrs);
\endcode
@@ -258,7 +258,7 @@ There are three dw::core::View implementations for FLTK:
overview of the whole canvas.
<li> dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support
- scrolling. It is used for HTML buttons, see
+ scrolling. It is used for HTML buttons, see
dw::fltk::ui::FltkComplexButtonResource and especially
dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details.
</ul>
diff --git a/doc/dw-viewport-with-scrollbar.png b/doc/dw-viewport-with-scrollbar.png
new file mode 100644
index 00000000..7ac62de3
--- /dev/null
+++ b/doc/dw-viewport-with-scrollbar.png
Binary files differ
diff --git a/doc/dw-viewport-without-scrollbar.png b/doc/dw-viewport-without-scrollbar.png
new file mode 100644
index 00000000..8aa20fec
--- /dev/null
+++ b/doc/dw-viewport-without-scrollbar.png
Binary files differ
diff --git a/doc/dw-widget-sizes.doc b/doc/dw-widget-sizes.doc
index 989eb83c..c93ce541 100644
--- a/doc/dw-widget-sizes.doc
+++ b/doc/dw-widget-sizes.doc
@@ -102,7 +102,7 @@ passed to the calles are
</ul>
Generally, the values should define the available space for the
-widget.
+widget.
A widget, which depends on size hints, should call
dw::core::Widget::queueResize, when apropriate.
@@ -183,4 +183,4 @@ This way, a widget can exactly keep track on size changes, and so
implement resizing in a faster way. A good example on how to use this
is dw::Textblock.
-*/ \ No newline at end of file
+*/
diff --git a/doc/fltk-problems.doc b/doc/fltk-problems.doc
index 3e8c401f..6f20c44d 100644
--- a/doc/fltk-problems.doc
+++ b/doc/fltk-problems.doc
@@ -5,19 +5,6 @@
Current problems:
<ul>
-<li> dw::fltk::FltkViewport::draw should only draw the region, for which e.g.
- an expose event was received.
-
-<li> dw::fltk::FltkViewport::queueDraw will collect data, which has to be
- redrawn. Currently, it calls redraw (DAMAGE_EXPOSE), can this be changed,
- so that dw::fltk::FltkViewport::draw will distinguish between the two
- cases?
-
-<li> For Scrolling, something similar applies, only parts of the viewport
- have to be redrawn.
-
-<li> Also for Scrolling, it is necessary to copy parts of the window.
-
<li> How should dw::fltk::FltkViewport::cancelQueueDraw be implemented?
<li> If the value of a scrollbar is changed by the program, not the user,
@@ -27,12 +14,7 @@ Current problems:
<li> Also, the problems with the widgets seems to work. Also sure?
-<li> When drawing, clipping of 32 bit values seems to work.
-
-<li> Who is responsable for clearing before drawing?
-
-<li> The embedded buttons are not redrawn, when pressing the mouse button
- on them.
+<li> When drawing, clipping of 32 bit values is not working properly.
<li> The item group within a selection widget (menu) should not be selectable.
</ul>
@@ -74,11 +56,6 @@ perhaps different implementations for different window systems could
be used. Then, it is for X necessary to use GCs with clipping masks.
-<h2>Lower Priority</h2>
-
-There needs to be an XEmbed implementation.
-
-
<h2>dw::fltk::ui::ComplexButton</h2>
Unfortunately, FLTK does not provide a button with Group as parent, so
@@ -106,7 +83,7 @@ The following changes should be applied manually.
<h3>Changes in fltkcomplexbutton.hh</h3>
-First of all, the #define's for avoiding multiple includes:
+First of all, the \#define's for avoiding multiple includes:
\code
-#ifndef fltk_ComplexButton_h // fltk_Button_h formerly
@@ -133,7 +110,7 @@ at the end. Then, the namespace is changed:
at the beginning and
-\code
+\code
-}
+} // namespace ui
+} // namespace fltk
@@ -151,7 +128,7 @@ at the end. Most important, the base class is changed:
and
-\code
+\code
-class FL_API ComplexButton : public Widget {
+class ComplexButton: public ::fltk::Group
+{
@@ -167,10 +144,10 @@ namespace conflict:
<h3>Changes in fltkcomplexbutton.cc</h3>
-First, #include's:
+First, \#include's:
\code
-
+
#include <fltk/events.h>
#include <fltk/damage.h>
-#include <fltk/ComplexButton.h> // <fltk/Button.h> formerly
@@ -216,4 +193,4 @@ dw::fltk::ui::ComplexButton::draw()):
}
\endcode
-*/ \ No newline at end of file
+*/
diff --git a/doc/uml-legend.doc b/doc/uml-legend.doc
index 14a64e7d..90294ef6 100644
--- a/doc/uml-legend.doc
+++ b/doc/uml-legend.doc
@@ -119,10 +119,10 @@ In this example,
</ul>
-<h2>Template Instanciations</h2>
+<h2>Template Instantiations</h2>
-Template instanciations are shown as own classes/interfaces, the
-instanciation by the template is shown by a yellow dashed arrow:
+Template instantiations are shown as own classes/interfaces, the
+instantiation by the template is shown by a yellow dashed arrow:
\dot
digraph G {
diff --git a/doc/user_help.html b/doc/user_help.html
new file mode 100644
index 00000000..296b0c5a
--- /dev/null
+++ b/doc/user_help.html
@@ -0,0 +1,298 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title> Help for Dillo </title>
+</head>
+
+<body bgcolor='#778899' text='#000000'>
+
+ <table bgcolor="#70a0c0" border="1" cellpadding="5">
+ <tr><td><h1>Help for Dillo 2.2</h1>
+ </table>
+ <p>
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=5>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Basics:</font></h4>
+ </td></tr>
+
+ <tr><td WIDTH="100%" bgcolor="#70a0c0">
+ <ul>
+ <li>You can tell a link from plain content by the hand-shaped cursor.
+ <li> Besides browsing the web, Dillo also has basic file browsing
+ capabilities included. So, entering <a href="file:">file:</a> in your
+ Dillo URL window will give you the contents of your current
+ working directory, and <a href="file:~">file:~</a> entered in the
+ same place will point your Dillo browser right to your home
+ directory...
+ <li> Dillo, at this stage of development, is <u>not</u> not ready
+ to render pages that use <b><font color="#5040a0">frames</font></b>.
+ Nevertheless, it comes with a tiny handler (lynx/w3m-like) that will
+ let you choose which frame to visit, one by one.
+ <li> Dillo has <b><font color="#5040a0">context
+ sensitive menus</font></b> using the right mouse button
+ (available on pages, links, images, forms, the Back
+ and Forward buttons, and the bug meter).
+ <li> Some of the functions in Dillo are handled by independent
+ processes. For instance, downloads come through <em>wget</em>.
+ If Dillo exits, the download can continue (more details
+ below).
+ </ul>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Usage:</font></h4>
+ </td></tr>
+
+ <tr><td BGCOLOR="#70a0c0">
+ <ul>
+ <li> You can scroll around your Dillo main window using
+ CTRL+{PgUp|PgDwn|Home|End} or using the mouse middle button
+ or mouse wheel. If nothing happens when keys are pressed, try
+ <b> <font color="#5040a0">focusing</font></b> your Dillo main
+ window first by clicking it (not on any link!:)).
+ <li> You can use the space key as PgDn, and {'b' | 'B'} as PgUp.</li>
+ <li> Similarly, you can use "<b>,</b>" and "<b>.</b>" as shortcuts for
+ forward and backward buttons (mnemonic: those
+ keys are usually labeled "<" and ">").
+ <li> <b>Configuration:</b> If you want to change Dillo's
+ appearance or behaviour, look at the options in your
+ <b><font color="#5040a0">dillorc</font></b>
+ file (if you don't have a copy in your ~/.dillo/ directory, get it
+ <a href='http://www.dillo.org/dillorc'>here</a>).
+ <li> Clicking the "Reload" button always requests an end-to-end reload
+ of the page currently viewed, but it will *not* reload embedded
+ images during this process.
+ <li> Dialogs can be closed with the ESC key.
+ <li> If you want to try a different control panel, right-click over the
+ search button until you find one that suits your needs, then make
+ it the default in your <code>dillorc</code> file.
+ <li> The whole window area can be used to display the page. Just hit
+ Ctrl-Space!
+ </ul>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Downloads:</font></h4>
+ </td></tr>
+
+ <tr><td BGCOLOR="#70a0c0">
+ <p>
+ Downloads are made using <b><font color="#5040a0">wget</font></b>
+ with a FLTK2-based GUI wrapper, through the Dillo plugin (dpi) framework.
+ If you close the browser window, downloads will continue.
+ <p>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Find text:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ This one is very useful;
+ it can be found in the right-mouse-button menu<br>
+ Find text is tuned for speed so don't hesitate to use it even for minimal
+ searches.
+ <p>
+ <u>Semantics:</u>
+ <ul>
+ <li> You can search for substrings, words and sentences. </li>
+ <li> To find a substring or word, just enter its text. </li>
+ <li> To find a left-aligned substring, prepend it with a space. </li>
+ <li> To find a right-aligned substring, append a space to it.</li>
+ <li> To find full words only, prepend and append spaces to them. </li>
+ <li> To find a sentence, enter the words and remember that the above rules
+ apply for every word in it. </li>
+ </ul>
+ <p>
+ Dillo will scroll the page and highlight found text!
+ <p>
+ <small>Default shortcut: [CTRL]+"F".</small>
+
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Copy&amp;Paste:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Just hold down the left mouse button and move to select the
+ area to copy. To paste, go to the target application and
+ press the middle mouse button.
+ <p>
+ If you want to select more than one screen, hold the mouse button
+ down and scroll with PgUp, PgDn or the arrow keys.
+ <P>
+ If you want to paste an URL into Dillo, do it on the "clear-URL"
+ button (the "X" next to the location bar).
+ <p>
+ Note: If it doesn't work, please try again. There's a bug lurking there.
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Navigation history:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Currently, navigation history supports the navigation-stack model; just
+ right-click on the Back or Forward buttons and they will pop up!
+ <p> <u>Remember:</u>
+ <ul>
+ <li> These history menus are relative to the current page. </li>
+ <li> They show the page-title but the status bar shows the URL. </li>
+ <li> Left-click jumps to the selected item. </li>
+ <li> Middle-click opens the item in a new browser tab/window. </li>
+ </ul>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Cookies:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Due to privacy concerns, cookies are disabled by default.
+ That is, if you just compile and use dillo, it will reject
+ every single cookie sent to it!
+ <p>
+ If you want to enable cookies in dillo, just
+ <a href="http://www.dillo.org/Cookies.txt">set up a <code>cookiesrc</code>
+ file</a>.
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Tabs:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Dillo has tabbed browsing. Just middle click to open a link or submit a
+ form in a new tab. It will be automatically focused. If you want to
+ customize this behaviour, adjust these dillorc options:
+ <ul>
+ <li><code>middle_click_opens_new_tab</code>
+ <li><code>focus_new_tab</code>
+ </ul>
+ Press SHIFT to temporarily reverse the focusing behaviour.
+ <p>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Images-off mode:</font></h4>
+ </td></tr>
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ You can browse without images now:
+ <ul>
+ <li>There is an option in the Tools menu to disable automatic image loading.
+ An image's alt text (or <code>[IMG]</code> placeholder) will appear in the
+ page.
+ <li>If you want to load an individual image, left click on its text.
+ <li>You can set "no images" as the default mode in dillorc.
+ </ul>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Bookmarks:</font></h4>
+ </td></tr>
+
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Bookmarks are handled by the Dillo plugin
+ (<a href='http://www.dillo.org/dpi1.html'>dpi</a>) framework.
+ This should be transparent to the end user. Please note that:
+ <ul>
+ <li>It is a good idea to keep a backup of your
+ <code>~/.dillo/bm.txt</code>
+ <li>The server will stay alive after closing dillo.
+ <li>You can stop the server by sending it a KILL signal. Dillo
+ will automatically restart it when it is needed.
+ <li>If you don't have root access, install <em>dpid</em> and
+ the <em>dpis</em>
+ inside your <code>~/.dillo/</code> directory as explained
+ in the README file.
+ </ul>
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Bug Meter:</font></h4>
+ </td></tr>
+ <tr><td bgcolor="#70a0c0">
+ <p>
+ Dillo's <a href='http://www.dillo.org/help/bug_meter.html'>bug meter</a>
+ shows the number of
+ <b><font color="#5040a0">detected bugs</font></b> inside the
+ page. The bugs are caught at parsing time, so the
+ error messages also show the line where they occur and provide a
+ <b><font color="#5040a0">hint</font></b> of what was expected instead!
+ <p>
+ The primary purpose of the bug meter is to
+ <b><font color="#5040a0">help</font></b> webmasters and page
+ authors to polish the contents of their sites with a view to making
+ them standards-compliant.
+ <p>
+ The bug meter is located at the lower right corner of
+ Dillo. Left-click to see the messages, right-click for a menu.
+ </td></tr>
+ </table>
+
+ <table WIDTH="100%" BORDER=1 CELLSPACING=0 CELLPADDING=3>
+ <tr ALIGN=LEFT VALIGN=TOP><td bgcolor="wheat">
+ <h4><font color="green">Keyboard shortcuts:</font></h4>
+ </td></tr>
+ <tr><td bgcolor="#70a0c0">
+<table border=1 width="100%">
+<tr><th>Shortcut <th>Mnemonic <th>Function
+<tr><td>Ctrl-L <td>Location <td>enter a new URL
+<tr><td>Ctrl-F <td>Find <td>find text
+<tr><td>Ctrl-S <td>Search <td>search the web
+<tr><td>Ctrl-R <td>Reload <td>reload current page
+<tr><td>Ctrl-N <td>New <td>New browser window
+<tr><td>Ctrl-T <td>Tab <td>New tab
+<tr><td>Ctrl-Q <td>Quit <td>quit tab/window
+<tr><td>Ctrl-O <td>Open <td>Open file
+<tr><td>Ctrl-B <td>Bookmarks <td>view bookmarks
+<tr><td>Ctrl-Space <td>"more space!" <td>Hide/show controls
+<tr><td>Back or "<b>,</b>" <td>< <td>previous page
+<tr><td>Shift-Back or "<b>.</b>" <td>> <td>next page
+<tr><td>Esc <td>escape <td>close dialog
+<tr><td>Alt-F <td>File <td>file menu
+<tr><td>Alt-X <td>eXit <td>exit Dillo
+<tr><td>Shift-Right <td>Right <td>Next tab
+<tr><td>Shift-Left <td>Left <td>Previous tab
+<tr><td>Ctrl-TabKey <td>TabKey <td>Next tab
+<tr><td>Shift-Ctrl- TabKey <td>TabKey <td>Previous tab
+</table>
+<p>
+You can change the bindings using a
+<code>~/.dillo/<a href="http://www.dillo.org/keysrc">keysrc</a></code>
+file.
+ </td></tr>
+ </table>
+
+</body>
+</html>
diff --git a/dpi/Makefile.am b/dpi/Makefile.am
index 43101359..34e07483 100644
--- a/dpi/Makefile.am
+++ b/dpi/Makefile.am
@@ -3,6 +3,7 @@ downloadsdir = $(libdir)/dillo/dpi/downloads
ftpdir = $(libdir)/dillo/dpi/ftp
httpsdir = $(libdir)/dillo/dpi/https
hellodir = $(libdir)/dillo/dpi/hello
+vsourcedir = $(libdir)/dillo/dpi/vsource
filedir = $(libdir)/dillo/dpi/file
cookiesdir = $(libdir)/dillo/dpi/cookies
datauridir = $(libdir)/dillo/dpi/datauri
@@ -11,6 +12,7 @@ downloads_PROGRAMS = downloads.dpi
ftp_PROGRAMS = ftp.filter.dpi
https_PROGRAMS = https.filter.dpi
hello_PROGRAMS = hello.filter.dpi
+vsource_PROGRAMS = vsource.filter.dpi
file_PROGRAMS = file.dpi
cookies_PROGRAMS = cookies.dpi
datauri_PROGRAMS = datauri.filter.dpi
@@ -20,11 +22,11 @@ downloads_dpi_LDADD = @LIBFLTK_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
ftp_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
https_filter_dpi_LDADD = @LIBSSL_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
hello_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-file_dpi_LDADD = @LIBPTHREAD_LIBS@ ../dpip/libDpip.a ../dlib/libDlib.a
+vsource_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
+file_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
cookies_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
datauri_filter_dpi_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-file_dpi_LDFLAGS = @LIBPTHREAD_LDFLAGS@
downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
@@ -32,6 +34,7 @@ downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
+vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h
diff --git a/dpi/bookmarks.c b/dpi/bookmarks.c
index e5447331..58fc59f5 100644
--- a/dpi/bookmarks.c
+++ b/dpi/bookmarks.c
@@ -44,11 +44,6 @@
#define _MSG(...)
#define MSG(...) printf("[bookmarks dpi]: " __VA_ARGS__)
-/* This one is tricky, some sources state it should include the byte
- * for the terminating NULL, and others say it shouldn't. */
-# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
- + strlen ((ptr)->sun_path))
-
#define DOCTYPE \
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
@@ -101,13 +96,13 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td>\n"
" <table width='100%' bgcolor='#b4b4b4'>\n"
" <tr>\n"
-" <td>&nbsp;Bookmarks::</td>\n"
-" <td width='100%' align='right'>\n"
+" <td> Bookmarks :: </td>\n"
+" <td align='right'>\n"
" [<a href='dpi:/bm/modify'>modify</a>]\n"
" </td></tr>\n"
" </table></td></tr>\n"
@@ -120,34 +115,36 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td>\n"
" <table width='100%' bgcolor='#b4b4b4'>\n"
" <tr>\n"
-" <td>&nbsp;Bookmarks :: modify</td></tr>\n"
+" <td> Bookmarks :: modify</td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
" </table></td></tr> \n"
"</table> \n"
"\n"
-"<form>\n"
+"<form action='modify'>\n"
"<table width='100%' border='1' cellpadding='0'>\n"
-" <tr><td>\n"
-" <table width='100%' bgcolor='teal'>\n"
-" <tr>\n"
-" <td><b>Select&nbsp;an&nbsp;operation&nbsp;</b></td>\n"
-" <td><select name='operation'>\n"
-" <option value='none' selected>--\n"
-" <option value='delete'>Delete\n"
-" <option value='move'>Move\n"
-" <option value='modify'>Modify\n"
-" <option value='add_sec'>Add Section\n"
-" <option value='add_url'>Add URL\n"
-" </select></td>\n"
-" <td><b>,&nbsp;mark&nbsp;its&nbsp;operands,&nbsp;and&nbsp;</b></td>\n"
-" <td><input type='submit' name='submit' value='submit.'></td>\n"
-" <td width='100%'></td>\n"
-" </tr>\n"
-" </table></td></tr>\n"
+" <tr style='background-color: teal'>\n"
+" <td>\n"
+" <b>Select an operation</b>\n"
+" <select name='operation'>\n"
+" <option value='none' selected>--\n"
+" <option value='delete'>Delete\n"
+" <option value='move'>Move\n"
+" <option value='modify'>Modify\n"
+" <option value='add_sec'>Add Section\n"
+" <option value='add_url'>Add URL\n"
+" </select>\n"
+" <b>, mark its operands, and</b>\n"
+" <input type='submit' name='submit' value='submit.'>\n"
+" </td>\n"
+" </tr>\n"
"</table>\n";
static const char *mainpage_sections_header =
@@ -248,15 +245,22 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add section\n"
-" </td></tr></table></td></tr>\n"
+" <tr>\n"
+" <td bgcolor='#b4b4b4'>\n"
+" Modify bookmarks :: add section\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
" <input type='hidden' name='operation' value='add_section'>\n"
"<table border='1' width='100%'>\n"
" <tr>\n"
@@ -288,15 +292,20 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: update\n"
-" </td></tr></table></td></tr>\n"
+" <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: update\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
"<input type='hidden' name='operation' value='modify2'>\n";
static const char *modifypage_update_title =
@@ -351,15 +360,20 @@ DOCTYPE
"<head>\n"
"<title>Bookmarks</title>\n"
"</head>\n"
-"<body bgcolor='#778899' link='black' vlink='brown'>\n"
+"<body id='dillo_bm' bgcolor='#778899' link='black' vlink='brown'>\n"
"<table border='1' cellpadding='0' width='100%'>\n"
" <tr><td colspan='2'>\n"
" <table bgcolor='#b4b4b4' width='100%'>\n"
-" <tr><td bgcolor='#b4b4b4'>&nbsp;Modify bookmarks:: add url\n"
-" </td></tr></table></td></tr>\n"
+" <tr><td bgcolor='#b4b4b4'> Modify bookmarks :: add url\n"
+" </td>\n"
+" <td align='right'>\n"
+" [<a href='dpi:/bm/'>cancel</a>]\n"
+" </td>\n"
+" </tr>\n"
+" </table></td></tr>\n"
"</table>\n"
"<br>\n"
-"<form>\n"
+"<form action='modify'>\n"
"<input type='hidden' name='operation' value='add_url2'>\n"
"<table border='1' width='100%'>\n"
" <tr>\n"
@@ -446,13 +460,13 @@ static void Unencode_str(char *e_str)
/*
* Send a short message to dillo's status bar.
*/
-static int Bmsrv_dpi_send_status_msg(SockHandler *sh, char *str)
+static int Bmsrv_dpi_send_status_msg(Dsh *sh, char *str)
{
int st;
char *d_cmd;
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_status_message", str);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
return st;
}
@@ -673,15 +687,29 @@ static void Bms_check_import(void)
"grep -i \"href\" %s | "
"sed -e 's/<li><A HREF=\"/s0 /' -e 's/\">/ /' -e 's/<.*$//' >> %s";
Dstr *dstr = dStr_new("");
+ int rc;
if (access(BmFile, F_OK) != 0) {
OldBmFile = dStrconcat(dGethomedir(), "/.dillo/bookmarks.html", NULL);
if (access(OldBmFile, F_OK) == 0) {
dStr_sprintf(dstr, cmd1, BmFile);
- system(dstr->str);
+ rc = system(dstr->str);
+ if (rc == 127) {
+ MSG("Bookmarks: /bin/sh could not be executed\n");
+ } else if (rc == -1) {
+ MSG("Bookmarks: process creation failure: %s\n",
+ dStrerror(errno));
+ }
dStr_sprintf(dstr, cmd2, OldBmFile, BmFile);
- system(dstr->str);
+ rc = system(dstr->str);
+ if (rc == 127) {
+ MSG("Bookmarks: /bin/sh could not be executed\n");
+ } else if (rc == -1) {
+ MSG("Bookmarks: process creation failure: %s\n",
+ dStrerror(errno));
+ }
+
dStr_free(dstr, TRUE);
dFree(OldBmFile);
}
@@ -835,7 +863,7 @@ static int Bms_save(void)
/*
* Add a new bookmark to DB :)
*/
-static int Bmsrv_add_bm(SockHandler *sh, char *url, char *title)
+static int Bmsrv_add_bm(Dsh *sh, char *url, char *title)
{
char *u_title;
char *msg="Added bookmark!";
@@ -883,13 +911,13 @@ static void Bmsrv_count_urls_and_sections(char *url, int *n_sec, int *n_url)
* Send a dpi reload request
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
+static int Bmsrv_send_reload_request(Dsh *sh, char *url)
{
int st;
char *d_cmd;
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "reload_request", url);
- st = sock_handler_write_str(sh, 1, d_cmd) ? 1 : 0;
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd) ? 1 : 0;
dFree(d_cmd);
return st;
}
@@ -898,7 +926,7 @@ static int Bmsrv_send_reload_request(SockHandler *sh, char *url)
* Send the HTML for the modify page
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page(SockHandler *sh)
+static int Bmsrv_send_modify_page(Dsh *sh)
{
static Dstr *dstr = NULL;
char *l_title;
@@ -910,25 +938,25 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
dstr = dStr_new("");
/* send modify page header */
- if (sock_handler_write_str(sh, 0, modifypage_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_header))
return 1;
/* write sections header */
- if (sock_handler_write_str(sh, 0, modifypage_sections_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_header))
return 1;
/* write sections */
for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
dStr_sprintf(dstr, modifypage_sections_item,
sec_node->section, sec_node->section, sec_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
/* write sections footer */
- if (sock_handler_write_str(sh, 0, modifypage_sections_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_sections_footer))
return 1;
/* send page middle */
- if (sock_handler_write_str(sh, 0, modifypage_middle1))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_middle1))
return 1;
/* send bookmark cards */
@@ -938,7 +966,7 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
dStr_sprintf(dstr, modifypage_section_card_header,
sec_node->section, l_title);
dFree(l_title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
/* send section's bookmarks */
@@ -946,18 +974,18 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
if (bm_node->section == sec_node->section) {
dStr_sprintf(dstr, modifypage_section_card_item,
bm_node->key, bm_node->url, bm_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
}
/* send card footer */
- if (sock_handler_write_str(sh, 0, modifypage_section_card_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_section_card_footer))
return 1;
}
/* finish page */
- if (sock_handler_write_str(sh, 1, modifypage_footer))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_footer))
return 1;
return 2;
@@ -967,10 +995,10 @@ static int Bmsrv_send_modify_page(SockHandler *sh)
* Send the HTML for the modify page for "add section"
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
+static int Bmsrv_send_modify_page_add_section(Dsh *sh)
{
/* send modify page2 */
- if (sock_handler_write_str(sh, 1, modifypage_add_section_page))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_section_page))
return 1;
return 2;
@@ -980,9 +1008,9 @@ static int Bmsrv_send_modify_page_add_section(SockHandler *sh)
* Send the HTML for the modify page for "add url"
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
+static int Bmsrv_send_modify_page_add_url(Dsh *sh)
{
- if (sock_handler_write_str(sh, 1, modifypage_add_url))
+ if (a_Dpip_dsh_write_str(sh, 1, modifypage_add_url))
return 1;
return 2;
}
@@ -994,7 +1022,7 @@ static int Bmsrv_send_modify_page_add_url(SockHandler *sh)
* - send the modify page for the marked urls and sections
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
+static int Bmsrv_send_modify_update(Dsh *sh, char *url)
{
static char *url1 = NULL;
static Dstr *dstr = NULL;
@@ -1016,7 +1044,7 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
}
/* send HTML here */
- if (sock_handler_write_str(sh, 0, modifypage_update_header))
+ if (a_Dpip_dsh_write_str(sh, 0, modifypage_update_header))
return 1;
/* Count number of marked urls and sections */
@@ -1024,8 +1052,8 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
if (n_sec) {
dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;sections:");
- sock_handler_write_str(sh, 0, dstr->str);
- sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
/* send items here */
p = strchr(url1, '?');
for (q = p; (q = strstr(q, "&s")); ++q) {
@@ -1035,17 +1063,17 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
if ((sec_node = Bms_get_sec(key))) {
dStr_sprintf(dstr, modifypage_update_item2,
sec_node->section, sec_node->title);
- sock_handler_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
}
}
}
- sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
}
if (n_url) {
dStr_sprintf(dstr, modifypage_update_title, "Update&nbsp;titles:");
- sock_handler_write_str(sh, 0, dstr->str);
- sock_handler_write_str(sh, 0, modifypage_update_item_header);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_header);
/* send items here */
p = strchr(url1, '?');
for (q = p; (q = strstr(q, "&url")); ++q) {
@@ -1055,13 +1083,13 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
bm_node = Bms_get(key);
dStr_sprintf(dstr, modifypage_update_item,
bm_node->key, bm_node->title, bm_node->url);
- sock_handler_write_str(sh, 0, dstr->str);
+ a_Dpip_dsh_write_str(sh, 0, dstr->str);
}
}
- sock_handler_write_str(sh, 0, modifypage_update_item_footer);
+ a_Dpip_dsh_write_str(sh, 0, modifypage_update_item_footer);
}
- sock_handler_write_str(sh, 1, modifypage_update_footer);
+ a_Dpip_dsh_write_str(sh, 1, modifypage_update_footer);
return 2;
}
@@ -1070,19 +1098,19 @@ static int Bmsrv_send_modify_update(SockHandler *sh, char *url)
* Make the modify-page and send it back
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_send_modify_answer(SockHandler *sh, char *url)
+static int Bmsrv_send_modify_answer(Dsh *sh, char *url)
{
char *d_cmd;
int st;
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
if (st != 0)
return 1;
/* Send HTTP header */
- if (sock_handler_write_str(sh, 0, Header) != 0) {
+ if (a_Dpip_dsh_write_str(sh, 0, Header) != 0) {
return 1;
}
@@ -1275,7 +1303,7 @@ static int Bmsrv_modify_add_section(char *url)
* Parse an "add url" request, and update the bm file.
* Return code: { 0:OK, 1:Abort }
*/
-static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
+static int Bmsrv_modify_add_url(Dsh *sh, char *s_url)
{
char *p, *q, *title, *u_title, *url;
int i;
@@ -1328,7 +1356,7 @@ static int Bmsrv_modify_add_url(SockHandler *sh, char *s_url)
* when it's wrong.
* Return code: { 0:OK, 2:Close }
*/
-static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
+static int Bmsrv_check_modify_request(Dsh *sh, char *url)
{
char *p, *msg;
int n_sec, n_url;
@@ -1393,7 +1421,7 @@ static int Bmsrv_check_modify_request(SockHandler *sh, char *url)
* Parse a and process a modify request.
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
+static int Bmsrv_process_modify_request(Dsh *sh, char *url)
{
/* check the provided parameters */
if (Bmsrv_check_modify_request(sh, url) != 0)
@@ -1459,7 +1487,7 @@ static int Bmsrv_process_modify_request(SockHandler *sh, char *url)
/*
* Send the current bookmarks page (in HTML)
*/
-static int send_bm_page(SockHandler *sh)
+static int send_bm_page(Dsh *sh)
{
static Dstr *dstr = NULL;
char *l_title;
@@ -1470,25 +1498,25 @@ static int send_bm_page(SockHandler *sh)
if (!dstr)
dstr = dStr_new("");
- if (sock_handler_write_str(sh, 0, mainpage_header))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_header))
return 1;
/* write sections header */
- if (sock_handler_write_str(sh, 0, mainpage_sections_header))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_header))
return 1;
/* write sections */
for (i = 0; (sec_node = dList_nth_data(B_secs, i)); ++i) {
dStr_sprintf(dstr, mainpage_sections_item,
sec_node->section, sec_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
/* write sections footer */
- if (sock_handler_write_str(sh, 0, mainpage_sections_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_sections_footer))
return 1;
/* send page middle */
- if (sock_handler_write_str(sh, 0, mainpage_middle1))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_middle1))
return 1;
/* send bookmark cards */
@@ -1498,7 +1526,7 @@ static int send_bm_page(SockHandler *sh)
dStr_sprintf(dstr, mainpage_section_card_header,
sec_node->section, l_title);
dFree(l_title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
/* send section's bookmarks */
@@ -1506,18 +1534,18 @@ static int send_bm_page(SockHandler *sh)
if (bm_node->section == sec_node->section) {
dStr_sprintf(dstr, mainpage_section_card_item,
bm_node->url, bm_node->title);
- if (sock_handler_write_str(sh, 0, dstr->str))
+ if (a_Dpip_dsh_write_str(sh, 0, dstr->str))
return 1;
}
}
/* send card footer */
- if (sock_handler_write_str(sh, 0, mainpage_section_card_footer))
+ if (a_Dpip_dsh_write_str(sh, 0, mainpage_section_card_footer))
return 1;
}
/* finish page */
- if (sock_handler_write_str(sh, 1, mainpage_footer))
+ if (a_Dpip_dsh_write_str(sh, 1, mainpage_footer))
return 1;
return 0;
@@ -1528,13 +1556,13 @@ static int send_bm_page(SockHandler *sh)
/*
* Parse a data stream (dpi protocol)
- * Note: Buf is a zero terminated string
+ * Note: Buf is a dpip token (zero terminated string)
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
+static int Bmsrv_parse_token(Dsh *sh, char *Buf)
{
static char *msg1=NULL, *msg2=NULL, *msg3=NULL;
- char *p, *cmd, *d_cmd, *url, *title, *msg;
+ char *cmd, *d_cmd, *url, *title, *msg;
size_t BufSize;
int st;
@@ -1545,29 +1573,28 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
msg3 = a_Dpip_build_cmd("cmd=%s msg=%s", "chat", "Ok, send it");
}
- if (!(p = strchr(Buf, '>'))) {
- /* Haven't got a full tag */
- MSG("Haven't got a full tag!\n");
+ if (sh->mode & DPIP_RAW) {
+ MSG("ERROR: Unhandled DPIP_RAW mode!\n");
return 1;
}
BufSize = strlen(Buf);
- cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
+ cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
if (cmd && strcmp(cmd, "chat") == 0) {
dFree(cmd);
- msg = a_Dpip_get_attr(Buf, BufSize, "msg");
+ msg = a_Dpip_get_attr_l(Buf, BufSize, "msg");
if (*msg == 'H') {
/* "Hi server" */
- if (sock_handler_write_str(sh, 1, msg1))
+ if (a_Dpip_dsh_write_str(sh, 1, msg1))
return 1;
} else if (*msg == 'I') {
/* "I want to set abookmark" */
- if (sock_handler_write_str(sh, 1, msg2))
+ if (a_Dpip_dsh_write_str(sh, 1, msg2))
return 1;
} else if (*msg == 'S') {
/* "Sure" */
- if (sock_handler_write_str(sh, 1, msg3))
+ if (a_Dpip_dsh_write_str(sh, 1, msg3))
return 1;
}
dFree(msg);
@@ -1583,8 +1610,8 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
} else if (cmd && strcmp(cmd, "add_bookmark") == 0) {
dFree(cmd);
- url = a_Dpip_get_attr(Buf, BufSize, "url");
- title = a_Dpip_get_attr(Buf, BufSize, "title");
+ url = a_Dpip_get_attr_l(Buf, BufSize, "url");
+ title = a_Dpip_get_attr_l(Buf, BufSize, "title");
if (strlen(title) == 0) {
dFree(title);
title = dStrdup("(Untitled)");
@@ -1597,7 +1624,7 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
} else if (cmd && strcmp(cmd, "open_url") == 0) {
dFree(cmd);
- url = a_Dpip_get_attr(Buf, BufSize, "url");
+ url = a_Dpip_get_attr_l(Buf, BufSize, "url");
if (strcmp(url, "dpi:/bm/modify") == 0) {
st = Bmsrv_send_modify_answer(sh, url);
@@ -1611,13 +1638,13 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- st = sock_handler_write_str(sh, 1, d_cmd);
+ st = a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
if (st != 0)
return 1;
/* Send HTTP header */
- if (sock_handler_write_str(sh, 1, Header) != 0) {
+ if (a_Dpip_dsh_write_str(sh, 1, Header) != 0) {
return 1;
}
@@ -1625,8 +1652,9 @@ static int Bmsrv_parse_buf(SockHandler *sh, char *Buf)
if (st != 0) {
char *err =
DOCTYPE
- "<HTML><body> Error on the bookmarks server...</body></html>";
- if (sock_handler_write_str(sh, 1, err) != 0) {
+ "<HTML><body id='dillo_bm'> Error on the bookmarks server..."
+ " </body></html>";
+ if (a_Dpip_dsh_write_str(sh, 1, err) != 0) {
return 1;
}
}
@@ -1658,13 +1686,12 @@ static void termination_handler(int signum)
/*
* -- MAIN -------------------------------------------------------------------
*/
-int main (void) {
+int main(void) {
struct sockaddr_un spun;
- int temp_sock_descriptor;
+ int sock_fd, code;
socklen_t address_size;
- char *buf;
- int code;
- SockHandler *sh;
+ char *tok;
+ Dsh *sh;
/* Arrange the cleanup function for terminations via exit() */
atexit(cleanup);
@@ -1677,6 +1704,9 @@ int main (void) {
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
+ /* We may receive SIGPIPE (e.g. socket is closed early by our client) */
+ signal(SIGPIPE, SIG_IGN);
+
/* Initialize local data */
B_bms = dList_new(512);
B_secs = dList_new(32);
@@ -1687,30 +1717,40 @@ int main (void) {
MSG("(v.13): accepting connections...\n");
while (1) {
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
- if (temp_sock_descriptor == -1) {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
+ if (sock_fd == -1) {
perror("[accept]");
exit(1);
}
- /* create the SockHandler structure */
- sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
+ /* create the Dsh structure */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(tok = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(tok) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ exit(1);
+ }
+ dFree(tok);
while (1) {
code = 1;
- if ((buf = sock_handler_read(sh)) != NULL) {
+ if ((tok = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
/* Let's see what we fished... */
- code = Bmsrv_parse_buf(sh, buf);
+ code = Bmsrv_parse_token(sh, tok);
}
- if (code == 1)
- exit(1);
- else if (code == 2)
+ dFree(tok);
+
+ if (code != 0) {
+ /* socket is not operative (e.g. closed by client) */
break;
+ }
}
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
}/*while*/
}
diff --git a/dpi/cookies.c b/dpi/cookies.c
index b7641372..a5142224 100644
--- a/dpi/cookies.c
+++ b/dpi/cookies.c
@@ -13,19 +13,13 @@
*
*/
-/* Handling of cookies takes place here.
- * This implementation aims to follow RFC 2965:
- * http://www.ietf.org/rfc/rfc2965.txt
- */
-
-/*
- * TODO: Cleanup this code. Shorten some functions, order things,
- * add comments, remove leaks, etc.
- */
-
-/* TODO: this server is not assembling the received packets.
- * This means it currently expects dillo to send full dpi tags
- * within the socket; if that fails, everything stops.
+/* This is written to follow the HTTP State Working Group's
+ * draft-ietf-httpstate-cookie-01.txt.
+ *
+ * Info on cookies in the wild:
+ * http://www.ietf.org/mail-archive/web/http-state/current/msg00078.html
+ * And dates specifically:
+ * http://www.ietf.org/mail-archive/web/http-state/current/msg00128.html
*/
#ifdef DISABLE_COOKIES
@@ -42,6 +36,7 @@ int main(void)
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
+#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
@@ -63,13 +58,6 @@ int main(void)
#define _MSG(...)
#define MSG(...) printf("[cookies dpi]: " __VA_ARGS__)
-
-/* This one is tricky, some sources state it should include the byte
- * for the terminating NULL, and others say it shouldn't. */
-# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
- + strlen ((ptr)->sun_path))
-
-
/*
* a_List_add()
*
@@ -90,6 +78,9 @@ int main(void)
/* The maximum length of a line in the cookie file */
#define LINE_MAXLEN 4096
+#define MAX_DOMAIN_COOKIES 20
+#define MAX_TOTAL_COOKIES 1200
+
typedef enum {
COOKIE_ACCEPT,
COOKIE_ACCEPT_SESSION,
@@ -103,8 +94,8 @@ typedef struct {
typedef struct {
char *domain;
- Dlist *dlist;
-} CookieNode;
+ Dlist *cookies;
+} DomainNode;
typedef struct {
char *name;
@@ -112,20 +103,25 @@ typedef struct {
char *domain;
char *path;
time_t expires_at;
- uint_t version;
- char *comment;
- char *comment_url;
+ bool_t host_only;
bool_t secure;
bool_t session_only;
- Dlist *ports;
+ long last_used;
} CookieData_t;
+typedef struct {
+ Dsh *sh;
+ int status;
+} ClientInfo;
+
/*
* Local data
*/
-/* List of CookieNode. Each node holds a domain and its list of cookies */
-static Dlist *cookies;
+static Dlist *all_cookies;
+
+/* List of DomainNode. Each node holds a domain and its list of cookies */
+static Dlist *domains;
/* Variables for access control */
static CookieControl *ccontrol = NULL;
@@ -133,13 +129,19 @@ static int num_ccontrol = 0;
static int num_ccontrol_max = 1;
static CookieControlAction default_action = COOKIE_DENY;
+static long cookies_use_counter = 0;
static bool_t disabled;
static FILE *file_stream;
-static char *cookies_txt_header_str =
+static const char *const cookies_txt_header_str =
"# HTTP Cookie File\n"
-"# http://wp.netscape.com/newsref/std/cookie_spec.html\n"
-"# This is a generated file! Do not edit.\n\n";
+"# This is a generated file! Do not edit.\n"
+"# [domain subdomains path secure expiry_time name value]\n\n";
+/* The epoch is Jan 1, 1970. When there is difficulty in representing future
+ * dates, use the (by far) most likely last representable time in Jan 19, 2038.
+ */
+static struct tm cookies_epoch_tm = {0, 0, 0, 1, 0, 70, 0, 0, 0, 0, 0};
+static time_t cookies_epoch_time, cookies_future_time;
/*
* Forward declarations
@@ -147,33 +149,39 @@ static char *cookies_txt_header_str =
static CookieControlAction Cookies_control_check_domain(const char *domain);
static int Cookie_control_init(void);
-static void Cookies_parse_ports(int url_port, CookieData_t *cookie,
- const char *port_str);
-static char *Cookies_build_ports_str(CookieData_t *cookie);
-static char *Cookies_strip_path(const char *path);
static void Cookies_add_cookie(CookieData_t *cookie);
-static void Cookies_remove_cookie(CookieData_t *cookie);
static int Cookies_cmp(const void *a, const void *b);
/*
- * Compare function for searching a cookie node
+ * Compare function for searching a domain node
*/
-static int Cookie_node_cmp(const void *v1, const void *v2)
+static int Domain_node_cmp(const void *v1, const void *v2)
{
- const CookieNode *n1 = v1, *n2 = v2;
+ const DomainNode *n1 = v1, *n2 = v2;
- return strcmp(n1->domain, n2->domain);
+ return dStrcasecmp(n1->domain, n2->domain);
}
/*
- * Compare function for searching a cookie node by domain
+ * Compare function for searching a domain node by domain
*/
-static int Cookie_node_by_domain_cmp(const void *v1, const void *v2)
+static int Domain_node_by_domain_cmp(const void *v1, const void *v2)
{
- const CookieNode *node = v1;
+ const DomainNode *node = v1;
const char *domain = v2;
- return strcmp(node->domain, domain);
+ return dStrcasecmp(node->domain, domain);
+}
+
+/*
+ * Delete node. This will not free any cookies that might be in node->cookies.
+ */
+static void Cookies_delete_node(DomainNode *node)
+{
+ dList_remove(domains, node);
+ dFree(node->domain);
+ dList_free(node->cookies);
+ dFree(node);
}
/*
@@ -181,17 +189,22 @@ static int Cookie_node_by_domain_cmp(const void *v1, const void *v2)
* with the optional 'init_str' as its content.
*/
static FILE *Cookies_fopen(const char *filename, const char *mode,
- char *init_str)
+ const char *init_str)
{
FILE *F_in;
- int fd;
+ int fd, rc;
if ((F_in = fopen(filename, mode)) == NULL) {
/* Create the file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd != -1) {
- if (init_str)
- write(fd, init_str, strlen(init_str));
+ if (init_str) {
+ rc = write(fd, init_str, strlen(init_str));
+ if (rc == -1) {
+ MSG("Cookies: Could not write initial string to file %s: %s\n",
+ filename, dStrerror(errno));
+ }
+ }
close(fd);
MSG("Created file: %s\n", filename);
@@ -215,71 +228,37 @@ static void Cookies_free_cookie(CookieData_t *cookie)
dFree(cookie->value);
dFree(cookie->domain);
dFree(cookie->path);
- dFree(cookie->comment);
- dFree(cookie->comment_url);
- dList_free(cookie->ports);
dFree(cookie);
}
+static void Cookies_tm_init(struct tm *tm)
+{
+ tm->tm_sec = cookies_epoch_tm.tm_sec;
+ tm->tm_min = cookies_epoch_tm.tm_min;
+ tm->tm_hour = cookies_epoch_tm.tm_hour;
+ tm->tm_mday = cookies_epoch_tm.tm_mday;
+ tm->tm_mon = cookies_epoch_tm.tm_mon;
+ tm->tm_year = cookies_epoch_tm.tm_year;
+ tm->tm_isdst = cookies_epoch_tm.tm_isdst;
+}
+
/*
- * Initialize the cookies module
- * (The 'disabled' variable is writable only within Cookies_init)
+ * Read in cookies from 'stream' (cookies.txt)
*/
-static void Cookies_init()
+static void Cookies_load_cookies(FILE *stream)
{
- CookieData_t *cookie;
- char *filename;
char line[LINE_MAXLEN];
-#ifndef HAVE_LOCKF
- struct flock lck;
-#endif
- FILE *old_cookies_file_stream;
-
- /* Default setting */
- disabled = TRUE;
-
- /* Read and parse the cookie control file (cookiesrc) */
- if (Cookie_control_init() != 0) {
- MSG("Disabling cookies.\n");
- return;
- }
-
- /* Get a stream for the cookies file */
- filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
- file_stream = Cookies_fopen(filename, "r+", cookies_txt_header_str);
-
- dFree(filename);
-
- if (!file_stream) {
- MSG("ERROR: Can't open ~/.dillo/cookies.txt, disabling cookies\n");
- return;
- }
-
- /* Try to get a lock from the file descriptor */
-#ifdef HAVE_LOCKF
- disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
-#else /* POSIX lock */
- lck.l_start = 0; /* start at beginning of file */
- lck.l_len = 0; /* lock entire file */
- lck.l_type = F_WRLCK;
- lck.l_whence = SEEK_SET; /* absolute offset */
- disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
-#endif
- if (disabled) {
- MSG("The cookies file has a file lock: disabling cookies!\n");
- fclose(file_stream);
- return;
- }
-
- MSG("Enabling cookies as from cookiesrc...\n");
-
- cookies = dList_new(32);
+ all_cookies = dList_new(32);
+ domains = dList_new(32);
/* Get all lines in the file */
- while (!feof(file_stream)) {
+ while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, file_stream);
+ if ((fgets(line, LINE_MAXLEN, stream) == NULL) && ferror(stream)) {
+ MSG("Error while reading from cookies.txt: %s\n", dStrerror(errno));
+ break; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
@@ -290,7 +269,7 @@ static void Cookies_init()
* pieces[0] The domain name
* pieces[1] TRUE/FALSE: is the domain a suffix, or a full domain?
* pieces[2] The path
- * pieces[3] Is the cookie unsecure or secure (TRUE/FALSE)
+ * pieces[3] TRUE/FALSE: is the cookie for secure use only?
* pieces[4] Timestamp of expire date
* pieces[5] Name of the cookie
* pieces[6] Value of the cookie
@@ -298,27 +277,37 @@ static void Cookies_init()
CookieControlAction action;
char *piece;
char *line_marker = line;
-
- cookie = dNew0(CookieData_t, 1);
+ CookieData_t *cookie = dNew0(CookieData_t, 1);
cookie->session_only = FALSE;
- cookie->version = 0;
cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
- dStrsep(&line_marker, "\t"); /* we use domain always as sufix */
+ piece = dStrsep(&line_marker, "\t");
+ if (piece != NULL && piece[0] == 'F')
+ cookie->host_only = TRUE;
cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
piece = dStrsep(&line_marker, "\t");
if (piece != NULL && piece[0] == 'T')
cookie->secure = TRUE;
piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->expires_at = (time_t) strtol(piece, NULL, 10);
+ if (piece != NULL) {
+ /* There is some problem with simply putting the maximum value
+ * into tm.tm_sec (although a value close to it works).
+ */
+ long seconds = strtol(piece, NULL, 10);
+ struct tm tm;
+ Cookies_tm_init(&tm);
+ tm.tm_min += seconds / 60;
+ tm.tm_sec += seconds % 60;
+ cookie->expires_at = mktime(&tm);
+ } else {
+ cookie->expires_at = (time_t) -1;
+ }
cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->value = dStrdup(dStrsep(&line_marker, "\t"));
+ cookie->value = dStrdup(line_marker ? line_marker : "");
if (!cookie->domain || cookie->domain[0] == '\0' ||
!cookie->path || cookie->path[0] != '/' ||
- !cookie->name || cookie->name[0] == '\0' ||
- !cookie->value) {
+ !cookie->name || !cookie->value) {
MSG("Malformed line in cookies.txt file!\n");
Cookies_free_cookie(cookie);
continue;
@@ -336,81 +325,63 @@ static void Cookies_init()
Cookies_add_cookie(cookie);
}
}
+ MSG("Cookies loaded: %d.\n", dList_length(all_cookies));
+}
- filename = dStrconcat(dGethomedir(), "/.dillo/cookies", NULL);
- if ((old_cookies_file_stream = fopen(filename, "r")) != NULL) {
- MSG("WARNING: Reading old cookies file ~/.dillo/cookies too\n");
-
- /* Get all lines in the file */
- while (!feof(old_cookies_file_stream)) {
- line[0] = '\0';
- fgets(line, LINE_MAXLEN, old_cookies_file_stream);
-
- /* Remove leading and trailing whitespaces */
- dStrstrip(line);
-
- if (line[0] != '\0') {
- /*
- * Split the row into pieces using a tab as the delimiter.
- * pieces[0] The version this cookie was set as (0 / 1)
- * pieces[1] The domain name
- * pieces[2] A comma separated list of accepted ports
- * pieces[3] The path
- * pieces[4] Is the cookie unsecure or secure (0 / 1)
- * pieces[5] Timestamp of expire date
- * pieces[6] Name of the cookie
- * pieces[7] Value of the cookie
- * pieces[8] Comment
- * pieces[9] Comment url
- */
- CookieControlAction action;
- char *piece;
- char *line_marker = line;
-
- cookie = dNew0(CookieData_t, 1);
-
- cookie->session_only = FALSE;
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->version = strtol(piece, NULL, 10);
- cookie->domain = dStrdup(dStrsep(&line_marker, "\t"));
- Cookies_parse_ports(0, cookie, dStrsep(&line_marker, "\t"));
- cookie->path = dStrdup(dStrsep(&line_marker, "\t"));
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL && piece[0] == '1')
- cookie->secure = TRUE;
- piece = dStrsep(&line_marker, "\t");
- if (piece != NULL)
- cookie->expires_at = (time_t) strtol(piece, NULL, 10);
- cookie->name = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->value = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->comment = dStrdup(dStrsep(&line_marker, "\t"));
- cookie->comment_url = dStrdup(dStrsep(&line_marker, "\t"));
-
- if (!cookie->domain || cookie->domain[0] == '\0' ||
- !cookie->path || cookie->path[0] != '/' ||
- !cookie->name || cookie->name[0] == '\0' ||
- !cookie->value) {
- MSG("Malformed line in cookies file!\n");
- Cookies_free_cookie(cookie);
- continue;
- }
+/*
+ * Initialize the cookies module
+ * (The 'disabled' variable is writeable only within Cookies_init)
+ */
+static void Cookies_init()
+{
+ char *filename;
+#ifndef HAVE_LOCKF
+ struct flock lck;
+#endif
+ struct tm future_tm = {7, 14, 3, 19, 0, 138, 0, 0, 0, 0, 0};
- action = Cookies_control_check_domain(cookie->domain);
- if (action == COOKIE_DENY) {
- Cookies_free_cookie(cookie);
- continue;
- } else if (action == COOKIE_ACCEPT_SESSION) {
- cookie->session_only = TRUE;
- }
+ /* Default setting */
+ disabled = TRUE;
- /* Save cookie in memory */
- Cookies_add_cookie(cookie);
- }
- }
- fclose(old_cookies_file_stream);
+ cookies_epoch_time = mktime(&cookies_epoch_tm);
+ cookies_future_time = mktime(&future_tm);
+
+ /* Read and parse the cookie control file (cookiesrc) */
+ if (Cookie_control_init() != 0) {
+ MSG("Disabling cookies.\n");
+ return;
}
+
+ /* Get a stream for the cookies file */
+ filename = dStrconcat(dGethomedir(), "/.dillo/cookies.txt", NULL);
+ file_stream = Cookies_fopen(filename, "r+", cookies_txt_header_str);
+
dFree(filename);
+
+ if (!file_stream) {
+ MSG("ERROR: Can't open ~/.dillo/cookies.txt; disabling cookies\n");
+ return;
+ }
+
+ /* Try to get a lock from the file descriptor */
+#ifdef HAVE_LOCKF
+ disabled = (lockf(fileno(file_stream), F_TLOCK, 0) == -1);
+#else /* POSIX lock */
+ lck.l_start = 0; /* start at beginning of file */
+ lck.l_len = 0; /* lock entire file */
+ lck.l_type = F_WRLCK;
+ lck.l_whence = SEEK_SET; /* absolute offset */
+
+ disabled = (fcntl(fileno(file_stream), F_SETLK, &lck) == -1);
+#endif
+ if (disabled) {
+ MSG("The cookies file has a file lock; disabling cookies!\n");
+ fclose(file_stream);
+ return;
+ }
+ MSG("Enabling cookies as per cookiesrc...\n");
+
+ Cookies_load_cookies(file_stream);
}
/*
@@ -418,9 +389,10 @@ static void Cookies_init()
*/
static void Cookies_save_and_free()
{
- int i, fd;
- CookieNode *node;
+ int i, fd, saved = 0;
+ DomainNode *node;
CookieData_t *cookie;
+ time_t now;
#ifndef HAVE_LOCKF
struct flock lck;
@@ -429,33 +401,34 @@ static void Cookies_save_and_free()
if (disabled)
return;
+ now = time(NULL);
+
rewind(file_stream);
fd = fileno(file_stream);
- ftruncate(fd, 0);
- fprintf(file_stream, cookies_txt_header_str);
+ if (ftruncate(fd, 0) == -1)
+ MSG("Cookies: Truncate file stream failed: %s\n", dStrerror(errno));
+ fprintf(file_stream, "%s", cookies_txt_header_str);
/* Iterate cookies per domain, saving and freeing */
- while ((node = dList_nth_data(cookies, 0))) {
- for (i = 0; (cookie = dList_nth_data(node->dlist, i)); ++i) {
- if (!cookie->session_only) {
- /* char * ports_str = Cookies_build_ports_str(cookie); */
- fprintf(file_stream, "%s\tTRUE\t%s\t%s\t%ld\t%s\t%s\n",
+ while ((node = dList_nth_data(domains, 0))) {
+ for (i = 0; (cookie = dList_nth_data(node->cookies, i)); ++i) {
+ if (!cookie->session_only && difftime(cookie->expires_at, now) > 0) {
+ fprintf(file_stream, "%s\t%s\t%s\t%s\t%ld\t%s\t%s\n",
cookie->domain,
+ cookie->host_only ? "FALSE" : "TRUE",
cookie->path,
cookie->secure ? "TRUE" : "FALSE",
- (long)cookie->expires_at,
+ (long)difftime(cookie->expires_at, cookies_epoch_time),
cookie->name,
cookie->value);
- /* dFree(ports_str); */
+ saved++;
}
-
Cookies_free_cookie(cookie);
}
- dList_remove(cookies, node);
- dFree(node->domain);
- dList_free(node->dlist);
- dFree(node);
+ Cookies_delete_node(node);
}
+ dList_free(domains);
+ dList_free(all_cookies);
#ifdef HAVE_LOCKF
lockf(fd, F_ULOCK, 0);
@@ -468,34 +441,33 @@ static void Cookies_save_and_free()
fcntl(fileno(file_stream), F_SETLKW, &lck);
#endif
fclose(file_stream);
-}
-static char *months[] =
-{ "",
- "Jan", "Feb", "Mar",
- "Apr", "May", "Jun",
- "Jul", "Aug", "Sep",
- "Oct", "Nov", "Dec"
-};
+ MSG("Cookies saved: %d.\n", saved);
+}
/*
- * Take a months name and return a number between 1-12.
- * E.g. 'April' -> 4
+ * Take a month's name and return a number between 0-11.
+ * E.g. 'April' -> 3
*/
static int Cookies_get_month(const char *month_name)
{
+ static const char *const months[] =
+ { "Jan", "Feb", "Mar",
+ "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep",
+ "Oct", "Nov", "Dec"
+ };
int i;
- for (i = 1; i <= 12; i++) {
+ for (i = 0; i < 12; i++) {
if (!dStrncasecmp(months[i], month_name, 3))
return i;
}
- return 0;
+ return -1;
}
/*
- * Return a local timestamp from a GMT date string
- * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format.
+ * Accept: RFC-1123 | RFC-850 | ANSI asctime | Old Netscape format date string.
*
* Wdy, DD-Mon-YY HH:MM:SS GMT
* Wdy, DD-Mon-YYYY HH:MM:SS GMT
@@ -504,633 +476,734 @@ static int Cookies_get_month(const char *month_name)
* Tue May 21 13:46:22 1991\n
* Tue May 21 13:46:22 1991
*
- * (return 0 on malformed date string syntax)
+ * Let's add:
+ * Mon Jan 11 08:00:00 2010 GMT
+ *
+ * Return a pointer to a struct tm, or NULL on error.
+ *
+ * NOTE that the draft spec wants user agents to be more flexible in what
+ * they accept. For now, let's hack in special cases when they're encountered.
+ * Why? Because this function is currently understandable, and I don't want to
+ * abandon that (or at best decrease that -- see section 5.1.1) until there
+ * is known to be good reason.
*/
-static time_t Cookies_create_timestamp(const char *expires)
+static struct tm *Cookies_parse_date(const char *date)
{
- time_t ret;
- int day, month, year, hour, minutes, seconds;
- char *cp;
- char *E_msg =
- "Expire date is malformed!\n"
- " (should be RFC-1123 | RFC-850 | ANSI asctime)\n"
- " Ignoring cookie: ";
-
- cp = strchr(expires, ',');
- if (!cp && (strlen(expires) == 24 || strlen(expires) == 25)) {
+ struct tm *tm;
+ char *cp = strchr(date, ',');
+
+ if (!cp && strlen(date)>20 && date[13] == ':' && date[16] == ':') {
/* Looks like ANSI asctime format... */
- cp = (char *)expires;
- day = strtol(cp + 8, NULL, 10); /* day */
- month = Cookies_get_month(cp + 4); /* month */
- year = strtol(cp + 20, NULL, 10); /* year */
- hour = strtol(cp + 11, NULL, 10); /* hour */
- minutes = strtol(cp + 14, NULL, 10); /* minutes */
- seconds = strtol(cp + 17, NULL, 10); /* seconds */
-
- } else if (cp && (cp - expires == 3 || cp - expires > 5) &&
+ tm = dNew0(struct tm, 1);
+
+ cp = (char *)date;
+ tm->tm_mon = Cookies_get_month(cp + 4);
+ tm->tm_mday = strtol(cp + 8, NULL, 10);
+ tm->tm_hour = strtol(cp + 11, NULL, 10);
+ tm->tm_min = strtol(cp + 14, NULL, 10);
+ tm->tm_sec = strtol(cp + 17, NULL, 10);
+ tm->tm_year = strtol(cp + 20, NULL, 10) - 1900;
+
+ } else if (cp && (cp - date == 3 || cp - date > 5) &&
(strlen(cp) == 24 || strlen(cp) == 26)) {
/* RFC-1123 | RFC-850 format | Old Netscape format */
- day = strtol(cp + 2, NULL, 10);
- month = Cookies_get_month(cp + 5);
- year = strtol(cp + 9, &cp, 10);
- /* TODO: tricky, because two digits for year IS ambiguous! */
- year += (year < 70) ? 2000 : ((year < 100) ? 1900 : 0);
- hour = strtol(cp + 1, NULL, 10);
- minutes = strtol(cp + 4, NULL, 10);
- seconds = strtol(cp + 7, NULL, 10);
+ tm = dNew0(struct tm, 1);
+
+ tm->tm_mday = strtol(cp + 2, NULL, 10);
+ tm->tm_mon = Cookies_get_month(cp + 5);
+ tm->tm_year = strtol(cp + 9, &cp, 10);
+ /* tm_year is the number of years since 1900 */
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+ else if (tm->tm_year > 100)
+ tm->tm_year -= 1900;
+ tm->tm_hour = strtol(cp + 1, NULL, 10);
+ tm->tm_min = strtol(cp + 4, NULL, 10);
+ tm->tm_sec = strtol(cp + 7, NULL, 10);
} else {
- MSG("%s%s\n", E_msg, expires);
- return (time_t) 0;
+ tm = NULL;
+ MSG("In date \"%s\", format not understood.\n", date);
}
- /* Error checks --this may be overkill */
- if (!(day > 0 && day < 32 && month > 0 && month < 13 && year > 1970 &&
- hour >= 0 && hour < 24 && minutes >= 0 && minutes < 60 &&
- seconds >= 0 && seconds < 60)) {
- MSG("%s%s\n", E_msg, expires);
- return (time_t) 0;
+ /* Error checks. This may be overkill. */
+ if (tm &&
+ !(tm->tm_mday > 0 && tm->tm_mday < 32 && tm->tm_mon >= 0 &&
+ tm->tm_mon < 12 && tm->tm_year >= 70 && tm->tm_hour >= 0 &&
+ tm->tm_hour < 24 && tm->tm_min >= 0 && tm->tm_min < 60 &&
+ tm->tm_sec >= 0 && tm->tm_sec < 60)) {
+ MSG("Date \"%s\" values not in range.\n", date);
+ dFree(tm);
+ tm = NULL;
}
- /* Calculate local timestamp.
- * [stolen from Lynx... (http://lynx.browser.org)] */
- month -= 3;
- if (month < 0) {
- month += 12;
- year--;
- }
+ return tm;
+}
- day += (year - 1968) * 1461 / 4;
- day += ((((month * 153) + 2) / 5) - 672);
- ret = (time_t)((day * 60 * 60 * 24) +
- (hour * 60 * 60) +
- (minutes * 60) +
- seconds);
+/*
+ * Find the least recently used cookie among those in the provided list.
+ */
+static CookieData_t *Cookies_get_LRU(Dlist *cookies)
+{
+ int i, n = dList_length(cookies);
+ CookieData_t *lru = dList_nth_data(cookies, 0);
- MSG("Expires in %ld seconds, at %s",
- (long)ret - time(NULL), ctime(&ret));
+ for (i = 1; i < n; i++) {
+ CookieData_t *curr = dList_nth_data(cookies, i);
- return ret;
+ if (curr->last_used < lru->last_used)
+ lru = curr;
+ }
+ return lru;
}
/*
- * Parse a string containing a list of port numbers.
+ * Delete expired cookies.
+ * If node is given, only check those cookies.
+ * Note that nodes can disappear if all of their cookies were expired.
+ *
+ * Return the number of cookies that were expired.
*/
-static void Cookies_parse_ports(int url_port, CookieData_t *cookie,
- const char *port_str)
+static int Cookies_rm_expired_cookies(DomainNode *node)
{
- if ((!port_str || !port_str[0]) && url_port != 0) {
- /* There was no list, so only the calling urls port should be allowed. */
- if (!cookie->ports)
- cookie->ports = dList_new(1);
- dList_append(cookie->ports, INT2VOIDP(url_port));
- } else if (port_str[0] == '"' && port_str[1] != '"') {
- char *tok, *str;
- int port;
-
- str = dStrdup(port_str + 1);
- while ((tok = dStrsep(&str, ","))) {
- port = strtol(tok, NULL, 10);
- if (port > 0) {
- if (!cookie->ports)
- cookie->ports = dList_new(1);
- dList_append(cookie->ports, INT2VOIDP(port));
- }
+ Dlist *cookies = node ? node->cookies : all_cookies;
+ int removed = 0;
+ int i = 0, n = dList_length(cookies);
+ time_t now = time(NULL);
+
+ while (i < n) {
+ CookieData_t *c = dList_nth_data(cookies, i);
+
+ if (difftime(c->expires_at, now) < 0) {
+ DomainNode *currnode = node ? node :
+ dList_find_sorted(domains, c->domain, Domain_node_by_domain_cmp);
+ dList_remove(currnode->cookies, c);
+ if (dList_length(currnode->cookies) == 0)
+ Cookies_delete_node(currnode);
+ dList_remove_fast(all_cookies, c);
+ Cookies_free_cookie(c);
+ n--;
+ removed++;
+ } else {
+ i++;
}
- dFree(str);
}
+ return removed;
}
/*
- * Build a string of the ports in 'cookie'.
+ * There are too many cookies. Choose one to remove and delete.
+ * If node is given, select from among its cookies only.
*/
-static char *Cookies_build_ports_str(CookieData_t *cookie)
+static void Cookies_too_many(DomainNode *node)
{
- Dstr *dstr;
- char *ret;
- void *data;
- int i;
-
- dstr = dStr_new("\"");
- for (i = 0; (data = dList_nth_data(cookie->ports, i)); ++i) {
- dStr_sprintfa(dstr, "%d,", VOIDP2INT(data));
- }
- /* Remove any trailing comma */
- if (dstr->len > 1)
- dStr_erase(dstr, dstr->len - 1, 1);
- dStr_append(dstr, "\"");
-
- ret = dstr->str;
- dStr_free(dstr, FALSE);
-
- return ret;
+ CookieData_t *lru = Cookies_get_LRU(node ? node->cookies : all_cookies);
+
+ MSG("Too many cookies!\n"
+ "Removing LRU cookie for \'%s\': \'%s=%s\'\n", lru->domain,
+ lru->name, lru->value);
+ if (!node)
+ node = dList_find_sorted(domains, lru->domain,Domain_node_by_domain_cmp);
+
+ dList_remove(node->cookies, lru);
+ dList_remove_fast(all_cookies, lru);
+ Cookies_free_cookie(lru);
+ if (dList_length(node->cookies) == 0)
+ Cookies_delete_node(node);
}
static void Cookies_add_cookie(CookieData_t *cookie)
{
Dlist *domain_cookies;
CookieData_t *c;
- CookieNode *node;
-
- /* Don't add an expired cookie */
- if (!cookie->session_only && cookie->expires_at < time(NULL)) {
- Cookies_free_cookie(cookie);
- return;
- }
+ DomainNode *node;
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
+ node = dList_find_sorted(domains, cookie->domain,Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
if (domain_cookies) {
- /* Respect the limit of 20 cookies per domain */
- if (dList_length(domain_cookies) >= 20) {
- MSG("There are too many cookies for this domain (%s)\n",
- cookie->domain);
- Cookies_free_cookie(cookie);
- return;
- }
-
- /* Remove any cookies with the same name and path */
- while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))){
- Cookies_remove_cookie(c);
+ /* Remove any cookies with the same name, path, and host-only values. */
+ while ((c = dList_find_custom(domain_cookies, cookie, Cookies_cmp))) {
+ dList_remove(domain_cookies, c);
+ dList_remove_fast(all_cookies, c);
+ Cookies_free_cookie(c);
}
}
- /* add the cookie into the respective domain list */
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
- if (!domain_cookies) {
- domain_cookies = dList_new(5);
- dList_append(domain_cookies, cookie);
- node = dNew(CookieNode, 1);
- node->domain = dStrdup(cookie->domain);
- node->dlist = domain_cookies;
- dList_insert_sorted(cookies, node, Cookie_node_cmp);
+ if ((cookie->expires_at == (time_t) -1) ||
+ (difftime(cookie->expires_at, time(NULL)) <= 0)) {
+ /*
+ * Don't add an expired cookie. Whether expiring now == expired, exactly,
+ * is arguable, but we definitely do not want to add a Max-Age=0 cookie.
+ */
+ _MSG("Goodbye, cookie %s=%s d:%s p:%s\n", cookie->name,
+ cookie->value, cookie->domain, cookie->path);
+ Cookies_free_cookie(cookie);
} else {
- dList_append(domain_cookies, cookie);
+ if (domain_cookies && dList_length(domain_cookies) >=MAX_DOMAIN_COOKIES){
+ int removed = Cookies_rm_expired_cookies(node);
+
+ if (removed == 0) {
+ Cookies_too_many(node);
+ } else if (removed >= MAX_DOMAIN_COOKIES) {
+ /* So many were removed that the node might have been deleted. */
+ node = dList_find_sorted(domains, cookie->domain,
+ Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
+ }
+ }
+ if (dList_length(all_cookies) >= MAX_TOTAL_COOKIES) {
+ if (Cookies_rm_expired_cookies(NULL) == 0) {
+ Cookies_too_many(NULL);
+ } else if (domain_cookies) {
+ /* Our own node might have just been deleted. */
+ node = dList_find_sorted(domains, cookie->domain,
+ Domain_node_by_domain_cmp);
+ domain_cookies = (node) ? node->cookies : NULL;
+ }
+ }
+
+ cookie->last_used = cookies_use_counter++;
+
+ /* Actually add the cookie! */
+ dList_append(all_cookies, cookie);
+
+ if (!domain_cookies) {
+ domain_cookies = dList_new(5);
+ dList_append(domain_cookies, cookie);
+ node = dNew(DomainNode, 1);
+ node->domain = dStrdup(cookie->domain);
+ node->cookies = domain_cookies;
+ dList_insert_sorted(domains, node, Domain_node_cmp);
+ } else {
+ dList_append(domain_cookies, cookie);
+ }
}
+ if (domain_cookies && (dList_length(domain_cookies) == 0))
+ Cookies_delete_node(node);
}
/*
- * Remove the cookie from the domain list.
- * If the domain list is empty, remove the node too.
- * Free the cookie.
+ * Return the attribute that is present at *cookie_str.
*/
-static void Cookies_remove_cookie(CookieData_t *cookie)
+static char *Cookies_parse_attr(char **cookie_str)
{
- CookieNode *node;
+ char *str;
+ uint_t len;
- node = dList_find_sorted(cookies, cookie->domain,Cookie_node_by_domain_cmp);
- if (node) {
- dList_remove(node->dlist, cookie);
- if (dList_length(node->dlist) == 0) {
- dList_remove(cookies, node);
- dFree(node->domain);
- dList_free(node->dlist);
- }
- } else {
- MSG("Attempting to remove a cookie that doesn't exist!\n");
- }
+ while (dIsspace(**cookie_str))
+ (*cookie_str)++;
- Cookies_free_cookie(cookie);
+ str = *cookie_str;
+ /* find '=' at end of attr, ';' after attr/val pair, '\0' end of string */
+ len = strcspn(str, "=;");
+ *cookie_str += len;
+
+ while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
+ len--;
+ return dStrndup(str, len);
}
/*
- * Return the attribute that is present at *cookie_str. This function
- * will also attempt to advance cookie_str past any equal-sign.
+ * Get the value in *cookie_str.
*/
-static char *Cookies_parse_attr(char **cookie_str)
+static char *Cookies_parse_value(char **cookie_str)
{
- char *str = *cookie_str;
- uint_t i, end = 0;
- bool_t got_attr = FALSE;
-
- for (i = 0; ; i++) {
- switch (str[i]) {
- case ' ':
- case '\t':
- case '=':
- case ';':
- got_attr = TRUE;
- if (end == 0)
- end = i;
- break;
- case ',':
- *cookie_str = str + i;
- return dStrndup(str, i);
- break;
- case '\0':
- if (!got_attr) {
- end = i;
- got_attr = TRUE;
- }
- /* fall through! */
- default:
- if (got_attr) {
- *cookie_str = str + i;
- return dStrndup(str, end);
- }
- break;
- }
+ uint_t len;
+ char *str;
+
+ if (**cookie_str == '=') {
+ (*cookie_str)++;
+ while (dIsspace(**cookie_str))
+ (*cookie_str)++;
+
+ str = *cookie_str;
+ /* finds ';' after attr/val pair or '\0' at end of string */
+ len = strcspn(str, ";");
+ *cookie_str += len;
+
+ while (len && (str[len - 1] == ' ' || str[len - 1] == '\t'))
+ len--;
+ } else {
+ str = *cookie_str;
+ len = 0;
}
+ return dStrndup(str, len);
+}
- return NULL;
+/*
+ * Advance past any value
+ */
+static void Cookies_eat_value(char **cookie_str)
+{
+ if (**cookie_str == '=')
+ *cookie_str += strcspn(*cookie_str, ";");
}
/*
- * Get the value starting at *cookie_str.
- * broken_syntax: watch out for stupid syntax (comma in unquoted string...)
+ * Return the number of seconds by which our clock is ahead of the server's
+ * clock.
*/
-static char *Cookies_parse_value(char **cookie_str,
- bool_t broken_syntax,
- bool_t keep_quotes)
+static double Cookies_server_timediff(const char *server_date)
{
- uint_t i, end;
- char *str = *cookie_str;
-
- for (i = end = 0; !end; ++i) {
- switch (str[i]) {
- case ' ':
- case '\t':
- if (!broken_syntax && str[0] != '\'' && str[0] != '"') {
- *cookie_str = str + i + 1;
- end = 1;
- }
- break;
- case '\'':
- case '"':
- if (i != 0 && str[i] == str[0]) {
- char *tmp = str + i;
+ double ret = 0;
- while (*tmp != '\0' && *tmp != ';' && *tmp != ',')
- tmp++;
+ if (server_date) {
+ struct tm *server_tm = Cookies_parse_date(server_date);
- *cookie_str = (*tmp == ';') ? tmp + 1 : tmp;
+ if (server_tm) {
+ time_t server_time = mktime(server_tm);
- if (keep_quotes)
- i++;
- end = 1;
- }
- break;
- case '\0':
- *cookie_str = str + i;
- end = 1;
- break;
- case ',':
- if (str[0] != '\'' && str[0] != '"' && !broken_syntax) {
- /* A new cookie starts here! */
- *cookie_str = str + i;
- end = 1;
- }
- break;
- case ';':
- if (str[0] != '\'' && str[0] != '"') {
- *cookie_str = str + i + 1;
- end = 1;
- }
- break;
- default:
- break;
+ if (server_time != (time_t) -1)
+ ret = difftime(time(NULL), server_time);
+ dFree(server_tm);
}
}
- /* keep i as an index to the last char */
- --i;
+ return ret;
+}
- if ((str[0] == '\'' || str[0] == '"') && !keep_quotes) {
- return i > 1 ? dStrndup(str + 1, i - 1) : NULL;
- } else {
- return dStrndup(str, i);
+static void Cookies_unquote_string(char *str)
+{
+ if (str && str[0] == '\"') {
+ uint_t len = strlen(str);
+
+ if (len > 1 && str[len - 1] == '\"') {
+ str[len - 1] = '\0';
+ while ((*str = str[1]))
+ str++;
+ }
}
}
/*
- * Parse one cookie...
+ * Parse cookie. A cookie might look something like:
+ * "Name=Val; Domain=example.com; Max-Age=3600; HttpOnly"
*/
-static CookieData_t *Cookies_parse_one(int url_port, char **cookie_str)
+static CookieData_t *Cookies_parse(char *cookie_str, const char *server_date)
{
- CookieData_t *cookie;
- char *str = *cookie_str;
- char *attr;
- char *value;
- int num_attr = 0;
+ CookieData_t *cookie = NULL;
+ char *str = cookie_str;
+ bool_t first_attr = TRUE;
bool_t max_age = FALSE;
- bool_t discard = FALSE;
- bool_t error = FALSE;
-
- cookie = dNew0(CookieData_t, 1);
- cookie->session_only = TRUE;
+ bool_t expires = FALSE;
- /* Iterate until there is nothing left of the string OR we come
- * across a comma representing the start of another cookie */
- while (*str != '\0' && *str != ',') {
- if (error) {
- str++;
- continue;
- }
- /* Skip whitespace */
- while (isspace(*str))
- str++;
+ /* Iterate until there is nothing left of the string */
+ while (*str) {
+ char *attr;
+ char *value;
/* Get attribute */
attr = Cookies_parse_attr(&str);
- if (!attr) {
- MSG("Cannot parse cookie attribute!\n");
- error = TRUE;
- continue;
- }
/* Get the value for the attribute and store it */
- if (num_attr == 0) {
- /* The first attr, which always is the user supplied attr, may
- * have the same name as an ordinary attr. Hence this workaround. */
- cookie->name = dStrdup(attr);
- cookie->value = Cookies_parse_value(&str, FALSE, TRUE);
+ if (first_attr) {
+ if (*str != '=' || *attr == '\0') {
+ /* disregard nameless cookie */
+ dFree(attr);
+ return NULL;
+ }
+ cookie = dNew0(CookieData_t, 1);
+ cookie->name = attr;
+ cookie->value = Cookies_parse_value(&str);
+
+ /* let's arbitrarily initialise with a year for now */
+ time_t now = time(NULL);
+ struct tm *tm = gmtime(&now);
+ ++tm->tm_year;
+ cookie->expires_at = mktime(tm);
+ if (cookie->expires_at == (time_t) -1)
+ cookie->expires_at = cookies_future_time;
} else if (dStrcasecmp(attr, "Path") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
+ value = Cookies_parse_value(&str);
+ dFree(cookie->path);
cookie->path = value;
} else if (dStrcasecmp(attr, "Domain") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
+ value = Cookies_parse_value(&str);
+ dFree(cookie->domain);
cookie->domain = value;
- } else if (dStrcasecmp(attr, "Discard") == 0) {
- cookie->session_only = TRUE;
- discard = TRUE;
} else if (dStrcasecmp(attr, "Max-Age") == 0) {
- if (!discard) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
-
- if (value) {
- cookie->expires_at = time(NULL) + strtol(value, NULL, 10);
- cookie->session_only = FALSE;
- max_age = TRUE;
- dFree(value);
- } else {
- MSG("Cannot parse cookie Max-Age value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ value = Cookies_parse_value(&str);
+ if (isdigit(*value) || *value == '-') {
+ time_t now = time(NULL);
+ long age = strtol(value, NULL, 10);
+ struct tm *tm = gmtime(&now);
+
+ tm->tm_sec += age;
+ cookie->expires_at = mktime(tm);
+ if (age > 0 && cookie->expires_at == (time_t) -1) {
+ cookie->expires_at = cookies_future_time;
}
+ _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
+ expires = max_age = TRUE;
}
+ dFree(value);
} else if (dStrcasecmp(attr, "Expires") == 0) {
- if (!max_age && !discard) {
- MSG("Old netscape-style cookie...\n");
- value = Cookies_parse_value(&str, TRUE, FALSE);
- if (value) {
- cookie->expires_at = Cookies_create_timestamp(value);
- cookie->session_only = FALSE;
- dFree(value);
+ if (!max_age) {
+ value = Cookies_parse_value(&str);
+ Cookies_unquote_string(value);
+ _MSG("Expires attribute gives %s\n", value);
+ struct tm *tm = Cookies_parse_date(value);
+ if (tm) {
+ tm->tm_sec += Cookies_server_timediff(server_date);
+ cookie->expires_at = mktime(tm);
+ if (cookie->expires_at == (time_t) -1 && tm->tm_year >= 138) {
+ /* Just checking tm_year does not ensure that the problem was
+ * inability to represent a distant date...
+ */
+ cookie->expires_at = cookies_future_time;
+ }
+ _MSG("Cookie to expire at %s", ctime(&cookie->expires_at));
+ dFree(tm);
} else {
- MSG("Cannot parse cookie Expires value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ cookie->expires_at = (time_t) -1;
}
- }
- } else if (dStrcasecmp(attr, "Port") == 0) {
- value = Cookies_parse_value(&str, FALSE, TRUE);
- Cookies_parse_ports(url_port, cookie, value);
- dFree(value);
- } else if (dStrcasecmp(attr, "Comment") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
- cookie->comment = value;
- } else if (dStrcasecmp(attr, "CommentURL") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
- cookie->comment_url = value;
- } else if (dStrcasecmp(attr, "Version") == 0) {
- value = Cookies_parse_value(&str, FALSE, FALSE);
-
- if (value) {
- cookie->version = strtol(value, NULL, 10);
+ expires = TRUE;
dFree(value);
} else {
- MSG("Cannot parse cookie Version value!\n");
- dFree(attr);
- error = TRUE;
- continue;
+ Cookies_eat_value(&str);
}
} else if (dStrcasecmp(attr, "Secure") == 0) {
cookie->secure = TRUE;
+ Cookies_eat_value(&str);
+ } else if (dStrcasecmp(attr, "HttpOnly") == 0) {
+ Cookies_eat_value(&str);
} else {
- /* Oops! this can't be good... */
MSG("Cookie contains unknown attribute: '%s'\n", attr);
- dFree(attr);
- error = TRUE;
- continue;
+ Cookies_eat_value(&str);
}
- dFree(attr);
- num_attr++;
- }
-
- *cookie_str = (*str == ',') ? str + 1 : str;
+ if (first_attr)
+ first_attr = FALSE;
+ else
+ dFree(attr);
- if (!error && (!cookie->name || !cookie->value)) {
- MSG("Cookie missing name and/or value!\n");
- error = TRUE;
- }
- if (error) {
- Cookies_free_cookie(cookie);
- cookie = NULL;
+ if (*str == ';')
+ str++;
}
+ cookie->session_only = expires == FALSE;
return cookie;
}
/*
- * Iterate the cookie string until we catch all cookies.
- * Return Value: a list with all the cookies! (or NULL upon error)
+ * Compare cookies by host_only, name, and path. Return 0 if equal.
*/
-static Dlist *Cookies_parse_string(int url_port, char *cookie_string)
+static int Cookies_cmp(const void *a, const void *b)
{
- CookieData_t *cookie;
- Dlist *ret = NULL;
- char *str = cookie_string;
+ const CookieData_t *ca = a, *cb = b;
- /* The string may contain several cookies separated by comma.
- * We'll iterate until we've caught them all */
- while (*str) {
- cookie = Cookies_parse_one(url_port, &str);
+ return (ca->host_only != cb->host_only) ||
+ (strcmp(ca->name, cb->name) != 0) ||
+ (strcmp(ca->path, cb->path) != 0);
+}
- if (cookie) {
- if (!ret)
- ret = dList_new(4);
- dList_append(ret, cookie);
- } else {
- MSG("Malformed cookie field, ignoring cookie: %s\n", cookie_string);
- }
- }
+/*
+ * Is the domain an IP address?
+ */
+static bool_t Cookies_domain_is_ip(const char *domain)
+{
+ uint_t len;
- return ret;
+ if (!domain)
+ return FALSE;
+
+ len = strlen(domain);
+
+ if (len == strspn(domain, "0123456789.")) {
+ _MSG("an IPv4 address\n");
+ return TRUE;
+ }
+ if (*domain == '[' &&
+ (len == strspn(domain, "0123456789abcdefABCDEF:.[]"))) {
+ /* The precise format is shown in section 3.2.2 of rfc 3986 */
+ _MSG("an IPv6 address\n");
+ return TRUE;
+ }
+ return FALSE;
}
/*
- * Compare cookies by name and path (return 0 if equal)
+ * Check whether url_path path-matches cookie_path
+ *
+ * Note different user agents apparently vary in path-matching behaviour,
+ * but this is the recommended method at the moment.
*/
-static int Cookies_cmp(const void *a, const void *b)
+static bool_t Cookies_path_matches(const char *url_path,
+ const char *cookie_path)
{
- const CookieData_t *ca = a, *cb = b;
- int ret;
+ bool_t ret = TRUE;
+
+ if (!url_path || !cookie_path) {
+ ret = FALSE;
+ } else {
+ uint_t c_len = strlen(cookie_path);
+ uint_t u_len = strlen(url_path);
- if (!(ret = strcmp(ca->name, cb->name)))
- ret = strcmp(ca->path, cb->path);
+ ret = (!strncmp(cookie_path, url_path, c_len) &&
+ ((c_len == u_len) ||
+ (c_len > 0 && cookie_path[c_len - 1] == '/') ||
+ (url_path[c_len] == '/')));
+ }
return ret;
}
/*
- * Validate cookies domain against some security checks.
+ * If cookie path is not properly set, remedy that.
*/
-static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host,
- char *url_path)
+static void Cookies_validate_path(CookieData_t *cookie, const char *url_path)
{
- int dots, diff, i;
- bool_t is_ip;
-
- /* Make sure that the path is set to something */
if (!cookie->path || cookie->path[0] != '/') {
dFree(cookie->path);
- cookie->path = Cookies_strip_path(url_path);
+
+ if (url_path) {
+ uint_t len = strlen(url_path);
+
+ while (len && url_path[len] != '/')
+ len--;
+ cookie->path = dStrndup(url_path, len ? len : 1);
+ } else {
+ cookie->path = dStrdup("/");
+ }
}
+}
- /* If the server never set a domain, or set one without a leading
- * dot (which isn't allowed), we use the calling URL's hostname. */
- if (cookie->domain == NULL || cookie->domain[0] != '.') {
- dFree(cookie->domain);
- cookie->domain = dStrdup(host);
+/*
+ * Check whether host name A domain-matches host name B.
+ */
+static bool_t Cookies_domain_matches(char *A, char *B)
+{
+ int diff;
+
+ if (!A || !*A || !B || !*B)
+ return FALSE;
+
+ if (*B == '.')
+ B++;
+
+ /* Should we concern ourselves with trailing dots in matching (here or
+ * elsewhere)? The HTTP State people have found that most user agents
+ * don't, so: No.
+ */
+
+ if (!dStrcasecmp(A, B))
return TRUE;
- }
- /* Count the number of dots and also find out if it is an IP-address */
- is_ip = TRUE;
- for (i = 0, dots = 0; cookie->domain[i] != '\0'; i++) {
- if (cookie->domain[i] == '.')
- dots++;
- else if (!isdigit(cookie->domain[i]))
- is_ip = FALSE;
- }
+ if (Cookies_domain_is_ip(B))
+ return FALSE;
- /* A valid domain must have at least two dots in it */
- /* NOTE: this breaks cookies on localhost... */
- if (dots < 2) {
+ diff = strlen(A) - strlen(B);
+
+ if (diff > 0) {
+ /* B is the tail of A, and the match is preceded by a '.' */
+ return (dStrcasecmp(A + diff, B) == 0 && A[diff - 1] == '.');
+ } else {
return FALSE;
}
+}
- /* Now see if the url matches the domain */
- diff = strlen(host) - i;
- if (diff > 0) {
- if (dStrcasecmp(host + diff, cookie->domain))
- return FALSE;
-
- if (!is_ip) {
- /* "x.y.test.com" is not allowed to set cookies for ".test.com";
- * only an url of the form "y.test.com" would be. */
- while ( diff-- )
- if (host[diff] == '.')
- return FALSE;
+/*
+ * Based on the host, how many internal dots do we need in a cookie domain
+ * to make it valid? e.g., "org" is not on the list, so dillo.org is a safe
+ * cookie domain, but "uk" is on the list, so ac.uk is not safe.
+ *
+ * This is imperfect, but it's something. Specifically, checking for these
+ * TLDs is the solution that Konqueror used once upon a time, according to
+ * reports.
+ */
+static uint_t Cookies_internal_dots_required(const char *host)
+{
+ uint_t ret = 1;
+
+ if (host) {
+ int start, after, tld_len;
+
+ /* We may be able to trust the format of the host string more than
+ * I am here. Trailing dots and no dots are real possibilities, though.
+ */
+ after = strlen(host);
+ if (after > 0 && host[after - 1] == '.')
+ after--;
+ start = after;
+ while (start > 0 && host[start - 1] != '.')
+ start--;
+ tld_len = after - start;
+
+ if (tld_len > 0) {
+ /* These TLDs were chosen by examining the current publicsuffix list
+ * in January 2010 and picking out those where it was simplest for
+ * them to describe the situation by beginning with a "*.[tld]" rule.
+ */
+ const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
+ "eg","er","et","fj","fk","gt","gu","id",
+ "il","jm","ke","kh","kw","ml","mm","mt",
+ "mz","ni","np","nz","om","pg","py","qa",
+ "sv","tr","uk","uy","ve","ye","yu","za",
+ "zm","zw"};
+ uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
+
+ for (i = 0; i < tld_num; i++) {
+ if (strlen(tlds[i]) == (uint_t) tld_len &&
+ !dStrncasecmp(tlds[i], host + start, tld_len)) {
+ _MSG("TLD code matched %s\n", tlds[i]);
+ ret++;
+ break;
+ }
+ }
}
}
-
- return TRUE;
+ return ret;
}
/*
- * Strip of the filename from a full path
+ * Validate cookies domain against some security checks.
*/
-static char *Cookies_strip_path(const char *path)
+static bool_t Cookies_validate_domain(CookieData_t *cookie, char *host)
{
- char *ret;
- uint_t len;
+ uint_t i, internal_dots;
+
+ if (!cookie->domain) {
+ cookie->domain = dStrdup(host);
+ cookie->host_only = TRUE;
+ return TRUE;
+ }
- if (path) {
- len = strlen(path);
+ if (!Cookies_domain_matches(host, cookie->domain))
+ return FALSE;
- while (len && path[len] != '/')
- len--;
- ret = dStrndup(path, len + 1);
- } else {
- ret = dStrdup("/");
+ internal_dots = 0;
+ for (i = 1; i < strlen(cookie->domain) - 1; i++) {
+ if (cookie->domain[i] == '.')
+ internal_dots++;
}
- return ret;
+ /* All of this dots business is a weak hack.
+ * TODO: accept the publicsuffix.org list as an optional external file.
+ */
+ if (internal_dots < Cookies_internal_dots_required(host)) {
+ MSG("not enough dots in %s\n", cookie->domain);
+ return FALSE;
+ }
+
+ _MSG("host %s and domain %s is all right\n", host, cookie->domain);
+ return TRUE;
}
/*
* Set the value corresponding to the cookie string
+ * Return value: 0 set OK, -1 disabled, -2 denied, -3 rejected.
*/
-static void Cookies_set(char *cookie_string, char *url_host,
- char *url_path, int url_port)
+static int Cookies_set(char *cookie_string, char *url_host,
+ char *url_path, char *server_date)
{
CookieControlAction action;
CookieData_t *cookie;
- Dlist *list;
- int i;
+ int ret = -1;
if (disabled)
- return;
+ return ret;
action = Cookies_control_check_domain(url_host);
if (action == COOKIE_DENY) {
MSG("denied SET for %s\n", url_host);
- return;
- }
+ ret = -2;
- if ((list = Cookies_parse_string(url_port, cookie_string))) {
- for (i = 0; (cookie = dList_nth_data(list, i)); ++i) {
- if (Cookies_validate_domain(cookie, url_host, url_path)) {
+ } else {
+ MSG("%s SETTING: %s\n", url_host, cookie_string);
+ ret = -3;
+ if ((cookie = Cookies_parse(cookie_string, server_date))) {
+ if (Cookies_validate_domain(cookie, url_host)) {
+ Cookies_validate_path(cookie, url_path);
if (action == COOKIE_ACCEPT_SESSION)
cookie->session_only = TRUE;
Cookies_add_cookie(cookie);
+ ret = 0;
} else {
- MSG("Rejecting cookie for %s from host %s path %s\n",
+ MSG("Rejecting cookie for domain %s from host %s path %s\n",
cookie->domain, url_host, url_path);
Cookies_free_cookie(cookie);
}
}
- dList_free(list);
}
+
+ return ret;
}
/*
- * Compare the cookie with the supplied data to see if it matches
+ * Compare the cookie with the supplied data to see whether it matches
*/
-static bool_t Cookies_match(CookieData_t *cookie, int port,
- const char *path, bool_t is_ssl)
+static bool_t Cookies_match(CookieData_t *cookie, const char *url_path,
+ bool_t host_only_val, bool_t is_ssl)
{
- void *data;
- int i;
+ if (cookie->host_only != host_only_val)
+ return FALSE;
/* Insecure cookies matches both secure and insecure urls, secure
cookies matches only secure urls */
if (cookie->secure && !is_ssl)
return FALSE;
- /* Check that the cookie path is a subpath of the current path */
- if (strncmp(cookie->path, path, strlen(cookie->path)) != 0)
- return FALSE;
-
- /* Check if the port of the request URL matches any
- * of those set in the cookie */
- if (cookie->ports) {
- for (i = 0; (data = dList_nth_data(cookie->ports, i)); ++i) {
- if (VOIDP2INT(data) == port)
- return TRUE;
- }
+ if (!Cookies_path_matches(url_path, cookie->path))
return FALSE;
- }
/* It's a match */
return TRUE;
}
+static void Cookies_add_matching_cookies(const char *domain,
+ const char *url_path,
+ bool_t host_only_val,
+ Dlist *matching_cookies,
+ bool_t is_ssl)
+{
+ DomainNode *node = dList_find_sorted(domains, domain,
+ Domain_node_by_domain_cmp);
+ if (node) {
+ int i;
+ CookieData_t *cookie;
+ Dlist *domain_cookies = node->cookies;
+
+ for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
+ /* Remove expired cookie. */
+ if (difftime(cookie->expires_at, time(NULL)) < 0) {
+ _MSG("Goodbye, expired cookie %s=%s d:%s p:%s\n", cookie->name,
+ cookie->value, cookie->domain, cookie->path);
+ dList_remove(domain_cookies, cookie);
+ dList_remove_fast(all_cookies, cookie);
+ Cookies_free_cookie(cookie);
+ --i; continue;
+ }
+ /* Check if the cookie matches the requesting URL */
+ if (Cookies_match(cookie, url_path, host_only_val, is_ssl)) {
+ int j;
+ CookieData_t *curr;
+ uint_t path_length = strlen(cookie->path);
+
+ cookie->last_used = cookies_use_counter;
+
+ /* Longest cookies go first */
+ for (j = 0;
+ (curr = dList_nth_data(matching_cookies, j)) &&
+ strlen(curr->path) >= path_length;
+ j++) ;
+ dList_insert_pos(matching_cookies, cookie, j);
+ }
+ }
+
+ if (dList_length(domain_cookies) == 0)
+ Cookies_delete_node(node);
+ }
+}
+
/*
* Return a string that contains all relevant cookies as headers.
*/
static char *Cookies_get(char *url_host, char *url_path,
- char *url_scheme, int url_port)
+ char *url_scheme)
{
- char *domain_str, *q, *str, *path;
+ char *domain_str, *str;
CookieData_t *cookie;
Dlist *matching_cookies;
- CookieNode *node;
- Dlist *domain_cookies;
- bool_t is_ssl;
+ bool_t is_ssl, is_ip_addr, host_only_val;
+
Dstr *cookie_dstring;
int i;
@@ -1139,27 +1212,47 @@ static char *Cookies_get(char *url_host, char *url_path,
matching_cookies = dList_new(8);
- path = Cookies_strip_path(url_path);
-
/* Check if the protocol is secure or not */
is_ssl = (!dStrcasecmp(url_scheme, "https"));
- for (domain_str = (char *) url_host;
- domain_str != NULL && *domain_str;
- domain_str = strchr(domain_str+1, '.')) {
-
- node = dList_find_sorted(cookies, domain_str, Cookie_node_by_domain_cmp);
- domain_cookies = (node) ? node->dlist : NULL;
-
- for (i = 0; (cookie = dList_nth_data(domain_cookies, i)); ++i) {
- /* Remove expired cookie. */
- if (!cookie->session_only && cookie->expires_at < time(NULL)) {
- Cookies_remove_cookie(cookie);
- --i; continue;
- }
- /* Check if the cookie matches the requesting URL */
- if (Cookies_match(cookie, url_port, path, is_ssl)) {
- dList_append(matching_cookies, cookie);
+ is_ip_addr = Cookies_domain_is_ip(url_host);
+
+ /* If a cookie is set that lacks a Domain attribute, its domain is set to
+ * the server's host and the host_only flag is set for that cookie. Such a
+ * cookie can only be sent back to that host. Cookies with Domain attrs do
+ * not have the host_only flag set, and may be sent to subdomains. Domain
+ * attrs can have leading dots, which should be ignored for matching
+ * purposes.
+ */
+ host_only_val = FALSE;
+ if (!is_ip_addr) {
+ /* e.g., sub.example.com set a cookie with domain ".sub.example.com". */
+ domain_str = dStrconcat(".", url_host, NULL);
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ dFree(domain_str);
+ }
+ host_only_val = TRUE;
+ /* e.g., sub.example.com set a cookie with no domain attribute. */
+ Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ host_only_val = FALSE;
+ /* e.g., sub.example.com set a cookie with domain "sub.example.com". */
+ Cookies_add_matching_cookies(url_host, url_path, host_only_val,
+ matching_cookies, is_ssl);
+
+ if (!is_ip_addr) {
+ for (domain_str = strchr(url_host+1, '.');
+ domain_str != NULL && *domain_str;
+ domain_str = strchr(domain_str+1, '.')) {
+ /* e.g., sub.example.com set a cookie with domain ".example.com". */
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
+ if (domain_str[1]) {
+ domain_str++;
+ /* e.g., sub.example.com set a cookie with domain "example.com".*/
+ Cookies_add_matching_cookies(domain_str, url_path, host_only_val,
+ matching_cookies, is_ssl);
}
}
}
@@ -1167,36 +1260,24 @@ static char *Cookies_get(char *url_host, char *url_path,
/* Found the cookies, now make the string */
cookie_dstring = dStr_new("");
if (dList_length(matching_cookies) > 0) {
- CookieData_t *first_cookie = dList_nth_data(matching_cookies, 0);
dStr_sprintfa(cookie_dstring, "Cookie: ");
- if (first_cookie->version != 0)
- dStr_sprintfa(cookie_dstring, "$Version=\"%d\"; ",
- first_cookie->version);
-
-
for (i = 0; (cookie = dList_nth_data(matching_cookies, i)); ++i) {
- q = (cookie->version == 0 ? "" : "\"");
- dStr_sprintfa(cookie_dstring,
- "%s=%s; $Path=%s%s%s; $Domain=%s%s%s",
- cookie->name, cookie->value,
- q, cookie->path, q, q, cookie->domain, q);
- if (cookie->ports) {
- char *ports_str = Cookies_build_ports_str(cookie);
- dStr_sprintfa(cookie_dstring, "; $Port=%s", ports_str);
- dFree(ports_str);
- }
-
+ dStr_sprintfa(cookie_dstring, "%s=%s", cookie->name, cookie->value);
dStr_append(cookie_dstring,
dList_length(matching_cookies) > i + 1 ? "; " : "\r\n");
}
}
dList_free(matching_cookies);
- dFree(path);
str = cookie_dstring->str;
dStr_free(cookie_dstring, FALSE);
+
+ if (*str)
+ cookies_use_counter++;
+
+ MSG("%s GETTING: %s\n", url_host, str);
return str;
}
@@ -1216,11 +1297,10 @@ static int Cookie_control_init(void)
{
CookieControl cc;
FILE *stream;
- char *filename;
+ char *filename, *rc;
char line[LINE_MAXLEN];
char domain[LINE_MAXLEN];
char rule[LINE_MAXLEN];
- int i, j;
bool_t enabled = FALSE;
/* Get a file pointer */
@@ -1234,28 +1314,31 @@ static int Cookie_control_init(void)
/* Get all lines in the file */
while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, stream);
+ rc = fgets(line, LINE_MAXLEN, stream);
+ if (!rc && ferror(stream)) {
+ MSG("Error while reading rule from cookiesrc: %s\n",
+ dStrerror(errno));
+ break; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
if (line[0] != '\0' && line[0] != '#') {
- i = 0;
- j = 0;
+ int i = 0, j = 0;
/* Get the domain */
- while (!isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
domain[j++] = line[i++];
domain[j] = '\0';
/* Skip past whitespaces */
- i++;
- while (isspace(line[i]))
+ while (dIsspace(line[i]))
i++;
/* Get the rule */
j = 0;
- while (line[i] != '\0' && !isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
rule[j++] = line[i++];
rule[j] = '\0';
@@ -1277,8 +1360,17 @@ static int Cookie_control_init(void)
default_action = cc.action;
dFree(cc.domain);
} else {
+ int i;
+ uint_t len = strlen(cc.domain);
+
+ /* Insert into list such that longest rules come first. */
a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
- ccontrol[num_ccontrol++] = cc;
+ for (i = num_ccontrol++;
+ i > 0 && (len > strlen(ccontrol[i-1].domain));
+ i--) {
+ ccontrol[i] = ccontrol[i-1];
+ }
+ ccontrol[i] = cc;
}
if (cc.action != COOKIE_DENY)
@@ -1292,7 +1384,9 @@ static int Cookie_control_init(void)
}
/*
- * Check the rules for an appropriate action for this domain
+ * Check the rules for an appropriate action for this domain.
+ * The rules are ordered by domain length, with longest first, so the
+ * first match is the most specific.
*/
static CookieControlAction Cookies_control_check_domain(const char *domain)
{
@@ -1326,69 +1420,74 @@ static CookieControlAction Cookies_control_check_domain(const char *domain)
* Note: Buf is a zero terminated string
* Return code: { 0:OK, 1:Abort, 2:Close }
*/
-static int srv_parse_buf(SockHandler *sh, char *Buf, size_t BufSize)
+static int srv_parse_tok(Dsh *sh, ClientInfo *client, char *Buf)
{
- char *p, *cmd, *cookie, *host, *path, *scheme;
- int port, ret;
+ char *cmd, *cookie, *host, *path;
+ int ret = 1;
+ size_t BufSize = strlen(Buf);
+
+ cmd = a_Dpip_get_attr_l(Buf, BufSize, "cmd");
+
+ if (!cmd) {
+ /* abort */
+ } else if (client->status == 0) {
+ /* authenticate */
+ if (a_Dpip_check_auth(Buf) == 1) {
+ client->status = 1;
+ ret = 0;
+ }
+ } else if (strcmp(cmd, "DpiBye") == 0) {
+ dFree(cmd);
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ exit(0);
- if (!(p = strchr(Buf, '>'))) {
- /* Haven't got a full tag */
- MSG("Haven't got a full tag!\n");
- return 1;
- }
+ } else if (strcmp(cmd, "set_cookie") == 0) {
+ int st;
+ char *date;
- cmd = a_Dpip_get_attr(Buf, BufSize, "cmd");
+ cookie = a_Dpip_get_attr_l(Buf, BufSize, "cookie");
+ host = a_Dpip_get_attr_l(Buf, BufSize, "host");
+ path = a_Dpip_get_attr_l(Buf, BufSize, "path");
+ date = a_Dpip_get_attr_l(Buf, BufSize, "date");
- if (cmd && strcmp(cmd, "DpiBye") == 0) {
- dFree(cmd);
- MSG("Cookies dpi (pid %d): Got DpiBye.\n", (int)getpid());
- exit(0);
+ st = Cookies_set(cookie, host, path, date);
- } else if (cmd && strcmp(cmd, "set_cookie") == 0) {
dFree(cmd);
- cookie = a_Dpip_get_attr(Buf, BufSize, "cookie");
- host = a_Dpip_get_attr(Buf, BufSize, "host");
- path = a_Dpip_get_attr(Buf, BufSize, "path");
- p = a_Dpip_get_attr(Buf, BufSize, "port");
- port = strtol(p, NULL, 10);
- dFree(p);
-
- Cookies_set(cookie, host, path, port);
+ cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "set_cookie_answer",
+ st == 0 ? "ok" : "not set");
+ a_Dpip_dsh_write_str(sh, 1, cmd);
+ dFree(date);
dFree(path);
dFree(host);
dFree(cookie);
- return 2;
+ ret = 2;
- } else if (cmd && strcmp(cmd, "get_cookie") == 0) {
- dFree(cmd);
- scheme = a_Dpip_get_attr(Buf, BufSize, "scheme");
- host = a_Dpip_get_attr(Buf, BufSize, "host");
- path = a_Dpip_get_attr(Buf, BufSize, "path");
- p = a_Dpip_get_attr(Buf, BufSize, "port");
- port = strtol(p, NULL, 10);
- dFree(p);
-
- cookie = Cookies_get(host, path, scheme, port);
+ } else if (strcmp(cmd, "get_cookie") == 0) {
+ char *scheme = a_Dpip_get_attr_l(Buf, BufSize, "scheme");
+
+ host = a_Dpip_get_attr_l(Buf, BufSize, "host");
+ path = a_Dpip_get_attr_l(Buf, BufSize, "path");
+
+ cookie = Cookies_get(host, path, scheme);
dFree(scheme);
dFree(path);
dFree(host);
+ dFree(cmd);
cmd = a_Dpip_build_cmd("cmd=%s cookie=%s", "get_cookie_answer", cookie);
- if (sock_handler_write_str(sh, 1, cmd)) {
+ if (a_Dpip_dsh_write_str(sh, 1, cmd)) {
ret = 1;
} else {
- _MSG("sock_handler_write_str: SUCCESS cmd={%s}\n", cmd);
+ _MSG("a_Dpip_dsh_write_str: SUCCESS cmd={%s}\n", cmd);
ret = 2;
}
dFree(cookie);
- dFree(cmd);
-
- return ret;
}
+ dFree(cmd);
- return 0;
+ return ret;
}
/* -- Termination handlers ----------------------------------------------- */
@@ -1415,13 +1514,13 @@ static void termination_handler(int signum)
/*
* -- MAIN -------------------------------------------------------------------
*/
-int main (void) {
- struct sockaddr_un spun;
- int temp_sock_descriptor;
+int main(void) {
+ struct sockaddr_in sin;
socklen_t address_size;
+ ClientInfo *client;
+ int sock_fd, code;
char *buf;
- int code;
- SockHandler *sh;
+ Dsh *sh;
/* Arrange the cleanup function for terminations via exit() */
atexit(cleanup);
@@ -1441,36 +1540,42 @@ int main (void) {
exit(1);
/* some OSes may need this... */
- address_size = sizeof(struct sockaddr_un);
+ address_size = sizeof(struct sockaddr_in);
while (1) {
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
- if (temp_sock_descriptor == -1) {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &address_size);
+ if (sock_fd == -1) {
perror("[accept]");
exit(1);
}
- /* create the SockHandler structure */
- sh = sock_handler_new(temp_sock_descriptor,temp_sock_descriptor,8*1024);
+ /* create the Dsh structure */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+ client = dNew(ClientInfo,1);
+ client->sh = sh;
+ client->status = 0;
while (1) {
code = 1;
- if ((buf = sock_handler_read(sh)) != NULL) {
+ if ((buf = a_Dpip_dsh_read_token(sh, 1)) != NULL) {
/* Let's see what we fished... */
_MSG(" buf = {%s}\n", buf);
- code = srv_parse_buf(sh, buf, strlen(buf));
+ code = srv_parse_tok(sh, client, buf);
+ dFree(buf);
}
+
_MSG(" code = %d %s\n", code, code == 1 ? "EXIT" : "BREAK");
- if (code == 1)
+ if (code == 1) {
exit(1);
- else if (code == 2)
+ } else if (code == 2) {
break;
+ }
}
- _MSG("Closing SockHandler\n");
- sock_handler_close(sh);
- sock_handler_free(sh);
+ _MSG("Closing Dsh\n");
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
+ dFree(client);
}/*while*/
diff --git a/dpi/datauri.c b/dpi/datauri.c
index 5f3c79a7..6d7acfa7 100644
--- a/dpi/datauri.c
+++ b/dpi/datauri.c
@@ -15,6 +15,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
+#include <errno.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
@@ -22,75 +24,89 @@
/*
* Debugging macros
*/
+#define SILENT 1
#define _MSG(...)
-#define MSG(...) printf("[datauri dpi]: " __VA_ARGS__)
+#if SILENT
+ #define MSG(...)
+#else
+ #define MSG(...) fprintf(stderr, "[datauri dpi]: " __VA_ARGS__)
+#endif
/*
* Global variables
*/
-static SockHandler *sh = NULL;
+static Dsh *sh = NULL;
+static void b64strip_illegal_chars(unsigned char* str)
+{
+ unsigned char *p, *s = str;
+
+ MSG("len=%d{%s}\n", strlen((char*)str), str);
+
+ for (p = s; (*p = *s); ++s) {
+ if (isalnum(*p) || strchr("+/=", *p))
+ ++p;
+ }
+ MSG("len=%d{%s}\n", strlen((char *)str), str);
+}
static int b64decode(unsigned char* str)
{
- unsigned char *cur, *start;
- int d, dlast, phase;
- unsigned char c;
- static int table[256] = {
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
- 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
- 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
- -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
- 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
- -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
- };
-
- d = dlast = phase = 0;
- start = str;
- for (cur = str; *cur != '\0'; ++cur )
- {
- // jer: treat line endings as physical breaks.
- //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
- d = table[(int)*cur];
- if(d != -1)
- {
- switch(phase)
- {
- case 0:
- ++phase;
- break;
- case 1:
- c = ((dlast << 2) | ((d & 0x30) >> 4));
- *str++ = c;
- ++phase;
- break;
- case 2:
- c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
- *str++ = c;
- ++phase;
- break;
- case 3:
- c = (((dlast & 0x03 ) << 6) | d);
- *str++ = c;
- phase = 0;
- break;
- }
- dlast = d;
- }
- }
- *str = '\0';
- return str - start;
+ unsigned char *cur, *start;
+ int d, dlast, phase;
+ unsigned char c;
+ static int table[256] = {
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
+ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
+ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
+ };
+
+ d = dlast = phase = 0;
+ start = str;
+ for (cur = str; *cur != '\0'; ++cur ) {
+ // jer: treat line endings as physical breaks.
+ //if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
+ d = table[(int)*cur];
+ if (d != -1) {
+ switch(phase) {
+ case 0:
+ ++phase;
+ break;
+ case 1:
+ c = ((dlast << 2) | ((d & 0x30) >> 4));
+ *str++ = c;
+ ++phase;
+ break;
+ case 2:
+ c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
+ *str++ = c;
+ ++phase;
+ break;
+ case 3:
+ c = (((dlast & 0x03 ) << 6) | d);
+ *str++ = c;
+ phase = 0;
+ break;
+ }
+ dlast = d;
+ }
+ }
+ *str = '\0';
+ return str - start;
}
/* Modified from src/url.c --------------------------------------------------*/
@@ -108,7 +124,7 @@ static int Url_decode_hex_octet(const char *s)
hex[2] = 0;
hex_value = strtol(hex, &tail, 16);
if (tail - hex == 2)
- return hex_value;
+ return hex_value;
}
return -1;
}
@@ -151,16 +167,16 @@ static void send_decoded_data(const char *url, const char *mime_type,
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
- sock_handler_write(sh, 0, (char *)data, data_sz);
+ a_Dpip_dsh_write(sh, 0, (char *)data, data_sz);
}
static void send_failure_message(const char *url, const char *mime_type,
@@ -178,24 +194,24 @@ static void send_failure_message(const char *url, const char *mime_type,
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, msg_mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, msg_mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
- sock_handler_write_str(sh, 0, msg);
+ a_Dpip_dsh_write_str(sh, 0, msg);
/* send some debug info */
snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
mime_type, (int)data_sz, data);
- sock_handler_write_str(sh, 0, buf);
+ a_Dpip_dsh_write_str(sh, 0, buf);
/* close page */
- sock_handler_write_str(sh, 0, "</body></html>");
+ a_Dpip_dsh_write_str(sh, 0, "</body></html>");
}
/*
@@ -253,7 +269,8 @@ static unsigned char *datauri_get_data(char *url, size_t *p_sz)
if (p) {
++p;
if (is_base64) {
- data = (unsigned char *)dStrdup(p);
+ data = (unsigned char *)Unescape_uri_str(p);
+ b64strip_illegal_chars(data);
*p_sz = (size_t) b64decode(data);
} else {
data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
@@ -273,19 +290,33 @@ int main(void)
{
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
unsigned char *data;
+ int rc;
size_t data_size = 0;
/* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
- chdir("/tmp");
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("[%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("Error, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
@@ -314,8 +345,8 @@ int main(void)
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/downloads.cc b/dpi/downloads.cc
index d2f3a54b..5aa7a87c 100644
--- a/dpi/downloads.cc
+++ b/dpi/downloads.cc
@@ -107,7 +107,7 @@ class DLItem {
enum {
ST_newline, ST_number, ST_discard, ST_copy
};
-
+
pid_t mPid;
int LogPipe[2];
char *shortname, *fullname;
@@ -291,10 +291,11 @@ static void prButton_scb(Widget *, void *cb_data)
DLItem::DLItem(const char *full_filename, const char *url, DLAction action)
{
struct stat ss;
- char *p, *esc_url;
+ const char *p;
+ char *esc_url;
if (pipe(LogPipe) < 0) {
- MSG("pipe, %s\n", strerror(errno));
+ MSG("pipe, %s\n", dStrerror(errno));
return;
}
/* Set FD to background */
@@ -513,7 +514,7 @@ void DLItem::log_text_add(const char *buf, ssize_t st)
if (isdigit(*q++ = *p)) {
// keep here
} else if (*p == 'K') {
- for(--q; isdigit(q[-1]); --q); log_state = ST_discard;
+ for (--q; isdigit(q[-1]); --q) ; log_state = ST_discard;
} else {
log_state = ST_copy;
}
@@ -582,7 +583,9 @@ void DLItem::update_size(int new_sz)
prBar->move(1);
} else {
prBar->showtext(true);
- double pos = 100.0 * (double)curr_bytesize / total_bytesize;
+ double pos = 100.0;
+ if (total_bytesize > 0)
+ pos *= (double)curr_bytesize / total_bytesize;
prBar->position(pos);
}
}
@@ -685,7 +688,7 @@ void DLItem::update()
/* Update curr_size */
if (stat(fullname, &ss) == -1) {
- MSG("stat, %s\n", strerror(errno));
+ MSG("stat, %s\n", dStrerror(errno));
return;
}
update_size((int)ss.st_size);
@@ -744,7 +747,7 @@ void DLItem::update()
/*! SIGCHLD handler
*/
static void raw_sigchld(int)
-{
+{
caught_sigchld = 1;
}
@@ -820,29 +823,6 @@ static void update_cb(void *data)
// DLWin ---------------------------------------------------------------------
/*
- * Read a single line from a socket and store it in a Dstr.
- */
-static ssize_t readline(int socket, Dstr ** msg)
-{
- ssize_t st;
- char buf[16384];
-
- /* can't use fread() */
- do
- st = read(socket, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- MSG("readline, %s\n", strerror(errno));
-
- dStr_truncate(*msg, 0);
- if (st > 0)
- dStr_append_l(*msg, buf, (int)st);
-
- return st;
-}
-
-/*
* Make a new name and place it in 'dl_dest'.
*/
static void make_new_name(char **dl_dest, const char *url)
@@ -873,33 +853,48 @@ static void make_new_name(char **dl_dest, const char *url)
*/
static void read_req_cb(int req_fd, void *)
{
- Dstr *tag;
struct sockaddr_un clnt_addr;
- int new_socket;
+ int sock_fd;
socklen_t csz;
struct stat sb;
- char *cmd = NULL, *url = NULL, *dl_dest = NULL;
+ Dsh *sh = NULL;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *dl_dest = NULL;
DLAction action = DL_ABORT; /* compiler happiness */
/* Initialize the value-result parameter */
csz = sizeof(struct sockaddr_un);
/* accept the request */
do {
- new_socket = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
- } while (new_socket == -1 && errno == EINTR);
- if (new_socket == -1) {
- MSG("accept, %s fd=%d\n", strerror(errno), req_fd);
+ sock_fd = accept(req_fd, (struct sockaddr *) &clnt_addr, &csz);
+ } while (sock_fd == -1 && errno == EINTR);
+ if (sock_fd == -1) {
+ MSG("accept, %s fd=%d\n", dStrerror(errno), req_fd);
return;
}
- //sigprocmask(SIG_BLOCK, &blockSC, NULL);
- tag = dStr_sized_new(64);
- readline(new_socket, &tag);
- close(new_socket);
- _MSG("Received tag={%s}\n", tag->str);
+ /* create a sock handler */
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s fd=%d\n", dStrerror(errno), sock_fd);
+ a_Dpip_dsh_close(sh);
+ goto end;
+ }
+ dFree(dpip_tag);
- if ((cmd = a_Dpip_get_attr(tag->str, (size_t)tag->len, "cmd")) == NULL) {
- MSG("Failed to parse 'cmd' in {%s}\n", tag->str);
+ /* Read request */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1))) {
+ MSG("can't read request: %s fd=%d\n", dStrerror(errno), sock_fd);
+ a_Dpip_dsh_close(sh);
+ goto end;
+ }
+ a_Dpip_dsh_close(sh);
+ _MSG("Received tag={%s}\n", dpip_tag);
+
+ if ((cmd = a_Dpip_get_attr(dpip_tag, "cmd")) == NULL) {
+ MSG("Failed to parse 'cmd' in {%s}\n", dpip_tag);
goto end;
}
if (strcmp(cmd, "DpiBye") == 0) {
@@ -910,12 +905,12 @@ static void read_req_cb(int req_fd, void *)
MSG("unknown command: '%s'. Aborting.\n", cmd);
goto end;
}
- if (!(url = a_Dpip_get_attr(tag->str,(size_t)tag->len, "url"))){
- MSG("Failed to parse 'url' in {%s}\n", tag->str);
+ if (!(url = a_Dpip_get_attr(dpip_tag, "url"))){
+ MSG("Failed to parse 'url' in {%s}\n", dpip_tag);
goto end;
}
- if (!(dl_dest = a_Dpip_get_attr(tag->str,(size_t)tag->len,"destination"))){
- MSG("Failed to parse 'destination' in {%s}\n", tag->str);
+ if (!(dl_dest = a_Dpip_get_attr(dpip_tag, "destination"))){
+ MSG("Failed to parse 'destination' in {%s}\n", dpip_tag);
goto end;
}
/* 'dl_dest' may be a directory */
@@ -934,7 +929,8 @@ end:
dFree(cmd);
dFree(url);
dFree(dl_dest);
- dStr_free(tag, TRUE);
+ dFree(dpip_tag);
+ a_Dpip_dsh_free(sh);
}
/*
@@ -976,7 +972,7 @@ void DLWin::add(const char *full_filename, const char *url, DLAction action)
} else if (f_pid < 0) {
perror("fork, ");
exit(1);
- } else {
+ } else {
/* father */
dl_win->show();
dl_item->pid(f_pid);
@@ -999,7 +995,7 @@ DLAction DLWin::check_filename(char **p_fullname)
return DL_NEWFILE;
ds = dStr_sized_new(128);
- dStr_sprintf(ds,
+ dStr_sprintf(ds,
"The file:\n %s (%d Bytes)\nalready exists. What do we do?",
*p_fullname, (int)ss.st_size);
ch = fltk::choice(ds->str, "Rename", "Continue", "Abort");
@@ -1020,18 +1016,14 @@ DLAction DLWin::check_filename(char **p_fullname)
}
/*
- * Add a new download request to the main window and
- * fork a child to do the job.
+ * Delete a download request from the main window.
*/
void DLWin::del(int n_item)
{
DLItem *dl_item = mDList->get(n_item);
- // Remove the widget from the scroll group
+ // Remove the widget from the packed group
mPG->remove(dl_item->get_widget());
- // Resize the scroll group
- mPG->resize(mWin->w(), 1);
-
mDList->del(n_item);
delete(dl_item);
}
@@ -1093,7 +1085,7 @@ DLWin::DLWin(int ww, int wh) {
mScroll->end();
mScroll->type(ScrollGroup::VERTICAL);
mWin->end();
- mWin->resizable(mPG);
+ mWin->resizable(mScroll);
mWin->callback(dlwin_esc_cb, NULL);
mWin->show();
diff --git a/dpi/dpiutil.c b/dpi/dpiutil.c
index b1affe95..e29d529f 100644
--- a/dpi/dpiutil.c
+++ b/dpi/dpiutil.c
@@ -57,6 +57,28 @@ char *Escape_uri_str(const char *str, const char *p_esc_set)
return p;
}
+/*
+ * Unescape %XX sequences in a string.
+ * Return value: a new unescaped string
+ */
+char *Unescape_uri_str(const char *s)
+{
+ char *p, *buf = dStrdup(s);
+
+ if (strchr(s, '%')) {
+ for (p = buf; (*p = *s); ++s, ++p) {
+ if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
+ *p = (isdigit(s[1]) ? (s[1] - '0') : toupper(s[1]) - 'A' + 10)*16;
+ *p += isdigit(s[2]) ? (s[2] - '0') : toupper(s[2]) - 'A' + 10;
+ s += 2;
+ }
+ }
+ }
+
+ return buf;
+}
+
+
static const char *unsafe_chars = "&<>\"'";
static const char *unsafe_rep[] =
{ "&amp;", "&lt;", "&gt;", "&quot;", "&#39;" };
@@ -141,130 +163,3 @@ char *Filter_smtp_hack(char *url)
return url;
}
-
-/* Streamed Sockets API (not mandatory) ----------------------------------*/
-
-/*
- * Create and initialize the SockHandler structure
- */
-SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz)
-{
- SockHandler *sh = dNew(SockHandler, 1);
-
- /* init descriptors and streams */
- sh->fd_in = fd_in;
- sh->fd_out = fd_out;
- sh->out = fdopen(fd_out, "w");
-
- /* init buffer */
- sh->buf_max = 8 * 1024;
- sh->buf = dNew(char, sh->buf_max);
- sh->buf_sz = 0;
- sh->flush_sz = flush_sz;
-
- return sh;
-}
-
-/*
- * Streamed write to socket
- * Return: 0 on success, 1 on error.
- */
-int sock_handler_write(SockHandler *sh, int flush,
- const char *Data, size_t DataSize)
-{
- int retval = 1;
-
- /* append to buf */
- while (sh->buf_max < sh->buf_sz + DataSize) {
- sh->buf_max <<= 1;
- sh->buf = dRealloc(sh->buf, sh->buf_max);
- }
- memcpy(sh->buf + sh->buf_sz, Data, DataSize);
- sh->buf_sz += DataSize;
-/*
- MSG("sh->buf=%p, sh->buf_sz=%d, sh->buf_max=%d, sh->flush_sz=%d\n",
- sh->buf, sh->buf_sz, sh->buf_max, sh->flush_sz);
-*/
-/**/
-#if 0
-{
- uint_t i;
- /* Test dpip's stream handling by chopping data into characters */
- for (i = 0; i < sh->buf_sz; ++i) {
- fputc(sh->buf[i], sh->out);
- fflush(sh->out);
- usleep(50);
- }
- if (i == sh->buf_sz) {
- sh->buf_sz = 0;
- retval = 0;
- }
-}
-#else
- /* flush data if necessary */
- if (flush || sh->buf_sz >= sh->flush_sz) {
- if (sh->buf_sz && fwrite (sh->buf, sh->buf_sz, 1, sh->out) != 1) {
- perror("[sock_handler_write]");
- } else {
- fflush(sh->out);
- sh->buf_sz = 0;
- retval = 0;
- }
-
- } else {
- retval = 0;
- }
-#endif
- return retval;
-}
-
-/*
- * Convenience function.
- */
-int sock_handler_write_str(SockHandler *sh, int flush, const char *str)
-{
- return sock_handler_write(sh, flush, str, strlen(str));
-}
-
-/*
- * Return a newlly allocated string with the contents read from the socket.
- */
-char *sock_handler_read(SockHandler *sh)
-{
- ssize_t st;
- char buf[16384];
-
- /* can't use fread() */
- do
- st = read(sh->fd_in, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- perror("[sock_handler_read]");
-
- return (st > 0) ? dStrndup(buf, (uint_t)st) : NULL;
-}
-
-/*
- * Close this socket for reading and writing.
- */
-void sock_handler_close(SockHandler *sh)
-{
- /* flush before closing */
- sock_handler_write(sh, 1, "", 0);
-
- fclose(sh->out);
- close(sh->fd_out);
-}
-
-/*
- * Free the SockHandler structure
- */
-void sock_handler_free(SockHandler *sh)
-{
- dFree(sh->buf);
- dFree(sh);
-}
-
-/* ------------------------------------------------------------------------ */
-
diff --git a/dpi/dpiutil.h b/dpi/dpiutil.h
index fa3dfb86..d939ddcb 100644
--- a/dpi/dpiutil.h
+++ b/dpi/dpiutil.h
@@ -5,7 +5,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
*/
@@ -28,43 +28,6 @@ extern "C" {
#endif /* __cplusplus */
-#define BUFLEN 256
-#define TOUT 300
-
-
-/* Streamed Sockets API (not mandatory) ----------------------------------*/
-
-typedef struct _SockHandler SockHandler;
-struct _SockHandler {
- int fd_in;
- int fd_out;
- /* FILE *in; --Unused. The stream functions block when reading. */
- FILE *out;
-
- char *buf; /* internal buffer */
- uint_t buf_sz; /* data size */
- uint_t buf_max; /* allocated size */
- uint_t flush_sz; /* max size before flush */
-};
-
-SockHandler *sock_handler_new(int fd_in, int fd_out, int flush_sz);
-int sock_handler_write(SockHandler *sh, int flush,
- const char *Data,size_t DataSize);
-int sock_handler_write_str(SockHandler *sh, int flush, const char *str);
-char *sock_handler_read(SockHandler *sh);
-void sock_handler_close(SockHandler *sh);
-void sock_handler_free(SockHandler *sh);
-
-#define sock_handler_printf(sh, flush, ...) \
- D_STMT_START { \
- Dstr *dstr = dStr_sized_new(128); \
- dStr_sprintf(dstr, __VA_ARGS__); \
- sock_handler_write(sh, flush, dstr->str, dstr->len); \
- dStr_free(dstr, 1); \
- } D_STMT_END
-
-/* ----------------------------------------------------------------------- */
-
/*
* Escape URI characters in 'esc_set' as %XX sequences.
* Return value: New escaped string.
@@ -72,6 +35,12 @@ void sock_handler_free(SockHandler *sh);
char *Escape_uri_str(const char *str, const char *p_esc_set);
/*
+ * Unescape %XX sequences in a string.
+ * Return value: a new unescaped string
+ */
+char *Unescape_uri_str(const char *str);
+
+/*
* Escape unsafe characters as html entities.
* Return value: New escaped string.
*/
diff --git a/dpi/file.c b/dpi/file.c
index 2398590b..1e70e9ef 100644
--- a/dpi/file.c
+++ b/dpi/file.c
@@ -15,14 +15,13 @@
* With new HTML layout.
*/
-#include <pthread.h>
-
#include <ctype.h> /* for tolower */
#include <errno.h> /* for errno */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -32,6 +31,7 @@
#include <fcntl.h>
#include <time.h>
#include <signal.h>
+#include <netinet/in.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
@@ -42,16 +42,31 @@
*/
#define _MSG(...)
#define MSG(...) printf("[file dpi]: " __VA_ARGS__)
+#define _MSG_RAW(...)
+#define MSG_RAW(...) printf(__VA_ARGS__)
#define MAXNAMESIZE 30
#define HIDE_DOTFILES TRUE
-enum {
- FILE_OK,
- FILE_NOT_FOUND,
- FILE_NO_ACCESS
-};
+/*
+ * Communication flags
+ */
+#define FILE_AUTH_OK 1 /* Authentication done */
+#define FILE_READ 2 /* Waiting data */
+#define FILE_WRITE 4 /* Sending data */
+#define FILE_DONE 8 /* Operation done */
+#define FILE_ERR 16 /* Operation error */
+
+
+typedef enum {
+ st_start = 10,
+ st_dpip,
+ st_http,
+ st_content,
+ st_done,
+ st_err
+} FileState;
typedef struct {
char *full_path;
@@ -67,42 +82,40 @@ typedef struct {
} DilloDir;
typedef struct {
- SockHandler *sh;
- int status;
+ Dsh *sh;
+ char *orig_url;
+ char *filename;
+ int file_fd;
+ off_t file_sz;
+ DilloDir *d_dir;
+ FileState state;
+ int err_code;
+ int flags;
int old_style;
- pthread_t thrID;
- int done;
} ClientInfo;
/*
* Forward references
*/
static const char *File_content_type(const char *filename);
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url);
-static int File_get_dir(ClientInfo *Client,
- const char *DirName,
- const char *orig_url);
/*
* Global variables
*/
-static volatile int DPIBYE = 0;
-static volatile int ThreadRunning = 0;
+static int DPIBYE = 0;
static int OLD_STYLE = 0;
/* A list for the clients we are serving */
static Dlist *Clients;
-/* a mutex for operations on clients */
-static pthread_mutex_t ClMut;
+/* Set of filedescriptors we're working on */
+fd_set read_set, write_set;
+
/*
* Close a file descriptor, but handling EINTR
*/
static void File_close(int fd)
{
- while (close(fd) < 0 && errno == EINTR)
+ while (fd >= 0 && close(fd) < 0 && errno == EINTR)
;
}
@@ -128,7 +141,7 @@ static const char *File_get_content_type_from_data(void *Data, size_t Size)
_MSG("File_get_content_type_from_data:: Size = %d\n", Size);
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -256,6 +269,8 @@ static void File_dillodir_free(DilloDir *Ddir)
int i;
FileInfo *finfo;
+ dReturn_if (Ddir == NULL);
+
for (i = 0; i < dList_length(Ddir->flist); ++i) {
finfo = dList_nth_data(Ddir->flist, i);
dFree(finfo->full_path);
@@ -270,7 +285,7 @@ static void File_dillodir_free(DilloDir *Ddir)
/*
* Output the string for parent directory
*/
-static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
+static void File_print_parent_dir(ClientInfo *client, const char *dirname)
{
if (strcmp(dirname, "/") != 0) { /* Not the root dir */
char *p, *parent, *HUparent, *Uparent;
@@ -284,7 +299,7 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
Uparent = Escape_uri_str(parent, NULL);
HUparent = Escape_html_str(Uparent);
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<a href='file:%s'>Parent directory</a>", HUparent);
dFree(HUparent);
dFree(Uparent);
@@ -295,20 +310,20 @@ static void File_print_parent_dir(ClientInfo *Client, const char *dirname)
/*
* Given a timestamp, output an HTML-formatted date string.
*/
-static void File_print_mtime(ClientInfo *Client, time_t mtime)
+static void File_print_mtime(ClientInfo *client, time_t mtime)
{
char *ds = ctime(&mtime);
/* Month, day and {hour or year} */
- if (Client->old_style) {
- sock_handler_printf(Client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
+ if (client->old_style) {
+ a_Dpip_dsh_printf(client->sh, 0, " %.3s %.2s", ds + 4, ds + 8);
if (time(NULL) - mtime > 15811200) {
- sock_handler_printf(Client->sh, 0, " %.4s", ds + 20);
+ a_Dpip_dsh_printf(client->sh, 0, " %.4s", ds + 20);
} else {
- sock_handler_printf(Client->sh, 0, " %.5s", ds + 11);
+ a_Dpip_dsh_printf(client->sh, 0, " %.5s", ds + 11);
}
} else {
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<td>%.3s&nbsp;%.2s&nbsp;%.5s", ds + 4, ds + 8,
/* (more than 6 months old) ? year : hour; */
(time(NULL) - mtime > 15811200) ? ds + 20 : ds + 11);
@@ -318,7 +333,7 @@ static void File_print_mtime(ClientInfo *Client, time_t mtime)
/*
* Return a HTML-line from file info.
*/
-static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
+static void File_info2html(ClientInfo *client, FileInfo *finfo, int n)
{
int size;
char *sizeunits;
@@ -361,10 +376,10 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
HUref = Escape_html_str(Uref);
Hname = Escape_html_str(name);
- if (Client->old_style) {
+ if (client->old_style) {
char *dots = ".. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..";
int ndots = MAXNAMESIZE - strlen(name);
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"%s<a href='%s'>%s</a>"
" %s"
" %-11s%4d %-5s",
@@ -373,15 +388,15 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
filecont, size, sizeunits);
} else {
- sock_handler_printf(Client->sh, 0,
+ a_Dpip_dsh_printf(client->sh, 0,
"<tr align=center %s><td>%s<td align=left><a href='%s'>%s</a>"
"<td>%s<td>%d&nbsp;%s",
(n & 1) ? "bgcolor=#dcdcdc" : "",
S_ISDIR (finfo->mode) ? ">" : " ", HUref, Hname,
filecont, size, sizeunits);
}
- File_print_mtime(Client, finfo->mtime);
- sock_handler_write_str(Client->sh, 0, "\n");
+ File_print_mtime(client, finfo->mtime);
+ a_Dpip_dsh_write_str(client->sh, 0, "\n");
dFree(Hname);
dFree(HUref);
@@ -389,79 +404,92 @@ static void File_info2html(ClientInfo *Client, FileInfo *finfo, int n)
}
/*
- * Read a local directory and translate it to html.
+ * Send the HTML directory page in HTTP.
*/
-static void File_transfer_dir(ClientInfo *Client,
- DilloDir *Ddir, const char *orig_url)
+static void File_send_dir(ClientInfo *client)
{
int n;
char *d_cmd, *Hdirname, *Udirname, *HUdirname;
+ DilloDir *Ddir = client->d_dir;
- /* Send DPI header */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Send page title */
- Udirname = Escape_uri_str(Ddir->dirname, NULL);
- HUdirname = Escape_html_str(Udirname);
- Hdirname = Escape_html_str(Ddir->dirname);
-
- sock_handler_printf(Client->sh, 0,
- "Content-Type: text/html\n\n"
- "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
- "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
- " <TITLE>file:%s</TITLE>\n</HEAD>\n"
- "<BODY><H1>Directory listing of %s</H1>\n",
- HUdirname, Hdirname, Hdirname);
- dFree(Hdirname);
- dFree(HUdirname);
- dFree(Udirname);
-
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "<pre>\n");
- }
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
+
+ } else if (client->state == st_dpip) {
+ /* send HTTP header and HTML top part */
+
+ /* Send page title */
+ Udirname = Escape_uri_str(Ddir->dirname, NULL);
+ HUdirname = Escape_html_str(Udirname);
+ Hdirname = Escape_html_str(Ddir->dirname);
+
+ a_Dpip_dsh_printf(client->sh, 0,
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
+ "<HTML>\n<HEAD>\n <BASE href='file:%s'>\n"
+ " <TITLE>file:%s</TITLE>\n</HEAD>\n"
+ "<BODY><H1>Directory listing of %s</H1>\n",
+ HUdirname, Hdirname, Hdirname);
+ dFree(Hdirname);
+ dFree(HUdirname);
+ dFree(Udirname);
+
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "<pre>\n");
+ }
- /* Output the parent directory */
- File_print_parent_dir(Client, Ddir->dirname);
+ /* Output the parent directory */
+ File_print_parent_dir(client, Ddir->dirname);
- /* HTML style toggle */
- sock_handler_write_str(Client->sh, 0,
- "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
+ /* HTML style toggle */
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "&nbsp;&nbsp;<a href='dpi:/file/toggle'>%</a>\n");
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "\n\n");
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "\n\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0,
+ "<br><br>\n"
+ "<table border=0 cellpadding=1 cellspacing=0"
+ " bgcolor=#E0E0E0 width=100%>\n"
+ "<tr align=center>\n"
+ "<td>\n"
+ "<td width=60%><b>Filename</b>"
+ "<td><b>Type</b>"
+ "<td><b>Size</b>"
+ "<td><b>Modified&nbsp;at</b>\n");
+ }
} else {
- sock_handler_write_str(Client->sh, 0,
- "<br><br>\n"
- "<table border=0 cellpadding=1 cellspacing=0"
- " bgcolor=#E0E0E0 width=100%>\n"
- "<tr align=center>\n"
- "<td>\n"
- "<td width=60%><b>Filename</b>"
- "<td><b>Type</b>"
- "<td><b>Size</b>"
- "<td><b>Modified&nbsp;at</b>\n");
+ a_Dpip_dsh_write_str(client->sh, 0, "<br><br>Directory is empty...");
}
- } else {
- sock_handler_write_str(Client->sh, 0, "<br><br>Directory is empty...");
- }
+ client->state = st_http;
- /* Output entries */
- for (n = 0; n < dList_length(Ddir->flist); ++n) {
- File_info2html(Client, dList_nth_data(Ddir->flist,n), n+1);
- }
+ } else if (client->state == st_http) {
+ /* send directories as HTML contents */
+ for (n = 0; n < dList_length(Ddir->flist); ++n) {
+ File_info2html(client, dList_nth_data(Ddir->flist,n), n+1);
+ }
- if (dList_length(Ddir->flist)) {
- if (Client->old_style) {
- sock_handler_write_str(Client->sh, 0, "</pre>\n");
- } else {
- sock_handler_write_str(Client->sh, 0, "</table>\n");
+ if (dList_length(Ddir->flist)) {
+ if (client->old_style) {
+ a_Dpip_dsh_write_str(client->sh, 0, "</pre>\n");
+ } else {
+ a_Dpip_dsh_write_str(client->sh, 0, "</table>\n");
+ }
}
- }
- sock_handler_write_str(Client->sh, 0, "</BODY></HTML>\n");
+ a_Dpip_dsh_write_str(client->sh, 1, "</BODY></HTML>\n");
+ client->state = st_content;
+ client->flags |= FILE_DONE;
+ }
}
/*
@@ -487,6 +515,8 @@ static const char *File_ext(const char *filename)
!dStrcasecmp(e, "htm") ||
!dStrcasecmp(e, "shtml")) {
return "text/html";
+ } else if (!dStrcasecmp(e, "txt")) {
+ return "text/plain";
} else {
return NULL;
}
@@ -517,54 +547,67 @@ static const char *File_content_type(const char *filename)
File_close(fd);
}
}
-
+ _MSG("File_content_type: name=%s ct=%s\n", filename, ct);
return ct;
}
/*
- * Try to stat the file and determine if it's readable.
+ * Send an error page
*/
-static void File_get(ClientInfo *Client, const char *filename,
- const char *orig_url)
+static void File_prepare_send_error_page(ClientInfo *client, int res,
+ const char *orig_url)
{
- int res;
- struct stat sb;
+ client->state = st_err;
+ client->err_code = res;
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+}
+
+/*
+ * Send an error page
+ */
+static void File_send_error_page(ClientInfo *client)
+{
+ const char *status;
char *d_cmd;
- Dstr *ds = NULL;
+ Dstr *body = dStr_sized_new(128);
- if (stat(filename, &sb) != 0) {
- /* stat failed, prepare a file-not-found error. */
- res = FILE_NOT_FOUND;
- } else if (S_ISDIR(sb.st_mode)) {
- /* set up for reading directory */
- res = File_get_dir(Client, filename, orig_url);
+ if (client->err_code == EACCES) {
+ status = "403 Forbidden";
+ } else if (client->err_code == ENOENT) {
+ status = "404 Not Found";
} else {
- /* set up for reading a file */
- res = File_get_file(Client, filename, &sb, orig_url);
+ /* good enough */
+ status = "500 Internal Server Error";
}
+ dStr_append(body, status);
+ dStr_append(body, "\n");
+ dStr_append(body, dStrerror(client->err_code));
- if (res == FILE_NOT_FOUND) {
- ds = dStr_sized_new(128);
- dStr_sprintf(ds, "%s Not Found: %s",
- S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
- } else if (res == FILE_NO_ACCESS) {
- ds = dStr_sized_new(128);
- dStr_sprintf(ds, "Access denied to %s: %s",
- S_ISDIR(sb.st_mode) ? "Directory" : "File", filename);
- }
- if (ds) {
- d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s","send_status_message",ds->str);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
- dStr_free(ds, 1);
- }
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 0, d_cmd);
+ dFree(d_cmd);
+
+ a_Dpip_dsh_printf(client->sh, 0,
+ "HTTP/1.1 %s\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%s",
+ status, body->len, body->str);
+ dStr_free(body, TRUE);
+
+ client->flags |= FILE_DONE;
}
/*
- *
+ * Scan the directory, sort and prepare to send it enclosed in HTTP.
*/
-static int File_get_dir(ClientInfo *Client,
- const char *DirName, const char *orig_url)
+static int File_prepare_send_dir(ClientInfo *client,
+ const char *DirName, const char *orig_url)
{
Dstr *ds_dirname;
DilloDir *Ddir;
@@ -578,90 +621,157 @@ static int File_get_dir(ClientInfo *Client,
Ddir = File_dillodir_new(ds_dirname->str);
dStr_free(ds_dirname, TRUE);
if (Ddir) {
- File_transfer_dir(Client, Ddir, orig_url);
- File_dillodir_free(Ddir);
- return FILE_OK;
+ /* looks ok, set things accordingly */
+ client->orig_url = dStrdup(orig_url);
+ client->d_dir = Ddir;
+ client->state = st_start;
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+ return 0;
} else
- return FILE_NO_ACCESS;
+ return EACCES;
+}
+
+/*
+ * Prepare to send HTTP headers and then the file itself.
+ */
+static int File_prepare_send_file(ClientInfo *client,
+ const char *filename,
+ const char *orig_url)
+{
+ int fd, res = -1;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* prepare a file-not-found error */
+ res = ENOENT;
+ } else if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0) {
+ /* prepare an error message */
+ res = errno;
+ } else {
+ /* looks ok, set things accordingly */
+ client->file_fd = fd;
+ client->file_sz = sb.st_size;
+ client->d_dir = NULL;
+ client->state = st_start;
+ client->filename = dStrdup(filename);
+ client->orig_url = dStrdup(orig_url);
+ client->flags &= ~FILE_READ;
+ client->flags |= FILE_WRITE;
+ res = 0;
+ }
+ return res;
+}
+
+/*
+ * Try to stat the file and determine if it's readable.
+ */
+static void File_get(ClientInfo *client, const char *filename,
+ const char *orig_url)
+{
+ int res;
+ struct stat sb;
+
+ if (stat(filename, &sb) != 0) {
+ /* stat failed, prepare a file-not-found error. */
+ res = ENOENT;
+ } else if (S_ISDIR(sb.st_mode)) {
+ /* set up for reading directory */
+ res = File_prepare_send_dir(client, filename, orig_url);
+ } else {
+ /* set up for reading a file */
+ res = File_prepare_send_file(client, filename, orig_url);
+ }
+ if (res != 0) {
+ File_prepare_send_error_page(client, res, orig_url);
+ }
}
/*
- * Send the MIME content/type and then send the file itself.
+ * Send HTTP headers and then the file itself.
*/
-static int File_get_file(ClientInfo *Client,
- const char *filename,
- struct stat *sb,
- const char *orig_url)
+static int File_send_file(ClientInfo *client)
{
+//#define LBUF 1
#define LBUF 16*1024
const char *ct;
const char *unknown_type = "application/octet-stream";
char buf[LBUF], *d_cmd, *name;
- int fd, st, namelen;
+ int st, st2, namelen;
bool_t gzipped = FALSE;
- if ((fd = open(filename, O_RDONLY | O_NONBLOCK)) < 0)
- return FILE_NO_ACCESS;
-
- /* Send DPI command */
- d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", orig_url);
- sock_handler_write_str(Client->sh, 1, d_cmd);
- dFree(d_cmd);
-
- /* Check for gzipped file */
- namelen = strlen(filename);
- if (namelen > 3 && !dStrcasecmp(filename + namelen - 3, ".gz")) {
- gzipped = TRUE;
- namelen -= 3;
- }
+ if (client->state == st_start) {
+ /* Send DPI command */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page",
+ client->orig_url);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
+ dFree(d_cmd);
+ client->state = st_dpip;
- /* Content-Type info is based on filename extension (with ".gz" removed).
- * If there's no known extension, perform data sniffing.
- * If this doesn't lead to a conclusion, use "application/octet-stream".
- */
- name = dStrndup(filename, namelen);
- if (!(ct = File_content_type(name)))
- ct = unknown_type;
- dFree(name);
-
- /* Send HTTP headers */
- if (gzipped) {
- sock_handler_write_str(Client->sh, 0, "Content-Encoding: gzip\n");
- }
- if (!gzipped || strcmp(ct, unknown_type)) {
- sock_handler_printf(Client->sh, 0, "Content-Type: %s\n", ct);
- } else {
- /* If we don't know type for gzipped data, let dillo figure it out. */
- }
- sock_handler_printf(Client->sh, 0, "Content-Length: %ld\n\n", sb->st_size);
+ } else if (client->state == st_dpip) {
+ /* send HTTP header */
- /* Send body -- raw file contents */
- do {
- if ((st = read(fd, buf, LBUF)) > 0) {
- if (sock_handler_write(Client->sh, 0, buf, (size_t)st) != 0)
- break;
- } else if (st < 0) {
- perror("[read]");
- if (errno == EINTR || errno == EAGAIN)
- continue;
+ /* Check for gzipped file */
+ namelen = strlen(client->filename);
+ if (namelen > 3 && !dStrcasecmp(client->filename + namelen - 3, ".gz")) {
+ gzipped = TRUE;
+ namelen -= 3;
+ }
+ /* Content-Type info is based on filename extension (with ".gz" removed).
+ * If there's no known extension, perform data sniffing.
+ * If this doesn't lead to a conclusion, use "application/octet-stream".
+ */
+ name = dStrndup(client->filename, namelen);
+ if (!(ct = File_content_type(name)))
+ ct = unknown_type;
+ dFree(name);
+
+ /* Send HTTP headers */
+ a_Dpip_dsh_write_str(client->sh, 0, "HTTP/1.1 200 OK\r\n");
+ if (gzipped) {
+ a_Dpip_dsh_write_str(client->sh, 0, "Content-Encoding: gzip\r\n");
+ }
+ if (!gzipped || strcmp(ct, unknown_type)) {
+ a_Dpip_dsh_printf(client->sh, 0, "Content-Type: %s\r\n", ct);
+ } else {
+ /* If we don't know type for gzipped data, let dillo figure it out. */
+ }
+ a_Dpip_dsh_printf(client->sh, 1,
+ "Content-Length: %ld\r\n"
+ "\r\n",
+ client->file_sz);
+ client->state = st_http;
+
+ } else if (client->state == st_http) {
+ /* Send body -- raw file contents */
+ if ((st = a_Dpip_dsh_tryflush(client->sh)) < 0) {
+ client->flags |= (st == -3) ? FILE_ERR : 0;
+ } else {
+ /* no pending data, let's send new data */
+ do {
+ st2 = read(client->file_fd, buf, LBUF);
+ } while (st2 < 0 && errno == EINTR);
+ if (st2 < 0) {
+ MSG("\nERROR while reading from file '%s': %s\n\n",
+ client->filename, dStrerror(errno));
+ client->flags |= FILE_ERR;
+ } else if (st2 == 0) {
+ client->state = st_content;
+ client->flags |= FILE_DONE;
+ } else {
+ /* ok to write */
+ st = a_Dpip_dsh_trywrite(client->sh, buf, st2);
+ client->flags |= (st == -3) ? FILE_ERR : 0;
+ }
}
- } while (st > 0);
-
- /* TODO: It may be better to send an error report to dillo instead of
- * calling exit() */
- if (st == -1) {
- MSG("ERROR while reading from file \"%s\", error was \"%s\"\n",
- filename, strerror(errno));
- exit(1);
}
- File_close(fd);
- return FILE_OK;
+ return 0;
}
/*
- * Given an hex octet (e3, 2F, 20), return the corresponding
+ * Given a hex octet (e3, 2F, 20), return the corresponding
* character if the octet is valid, and -1 otherwise
*/
static int File_parse_hex_octet(const char *s)
@@ -688,6 +798,8 @@ static char *File_normalize_path(const char *orig)
{
char *str = (char *) orig, *basename = NULL, *ret = NULL, *p;
+ dReturn_val_if (orig == NULL, ret);
+
/* Make sure the string starts with "file:/" */
if (strncmp(str, "file:/", 5) != 0)
return ret;
@@ -729,15 +841,15 @@ static char *File_normalize_path(const char *orig)
}
/*
- * Set the style flag and ask for a reload, so it shows inmediatly.
+ * Set the style flag and ask for a reload, so it shows immediately.
*/
-static void File_toggle_html_style(ClientInfo *Client)
+static void File_toggle_html_style(ClientInfo *client)
{
char *d_cmd;
OLD_STYLE = !OLD_STYLE;
d_cmd = a_Dpip_build_cmd("cmd=%s", "reload_request");
- sock_handler_write_str(Client->sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(client->sh, 1, d_cmd);
dFree(d_cmd);
}
@@ -746,6 +858,7 @@ static void File_toggle_html_style(ClientInfo *Client)
*/
static void termination_handler(int signum)
{
+ MSG("\nexit(signum), signum=%d\n\n", signum);
exit(signum);
}
@@ -757,170 +870,184 @@ static void termination_handler(int signum)
*/
static ClientInfo *File_add_client(int sock_fd)
{
- ClientInfo *NewClient;
-
- NewClient = dNew(ClientInfo, 1);
- NewClient->sh = sock_handler_new(sock_fd, sock_fd, 8*1024);
- NewClient->status = 0;
- NewClient->done = 0;
- NewClient->old_style = OLD_STYLE;
- pthread_mutex_lock(&ClMut);
- dList_append(Clients, NewClient);
- pthread_mutex_unlock(&ClMut);
- return NewClient;
-}
-
-/*
- * Get client record by number
- */
-static void *File_get_client_n(uint_t n)
-{
- void *client;
-
- pthread_mutex_lock(&ClMut);
- client = dList_nth_data(Clients, n);
- pthread_mutex_unlock(&ClMut);
-
- return client;
+ ClientInfo *new_client;
+
+ new_client = dNew(ClientInfo, 1);
+ new_client->sh = a_Dpip_dsh_new(sock_fd, sock_fd, 8*1024);
+ new_client->orig_url = NULL;
+ new_client->filename = NULL;
+ new_client->file_fd = -1;
+ new_client->file_sz = 0;
+ new_client->d_dir = NULL;
+ new_client->state = 0;
+ new_client->err_code = 0;
+ new_client->flags = FILE_READ;
+ new_client->old_style = OLD_STYLE;
+
+ dList_append(Clients, new_client);
+ return new_client;
}
/*
* Remove a client from the list.
*/
-static void File_remove_client_n(uint_t n)
+static void File_remove_client(ClientInfo *client)
{
- ClientInfo *Client;
-
- pthread_mutex_lock(&ClMut);
- Client = dList_nth_data(Clients, n);
- dList_remove(Clients, (void *)Client);
- pthread_mutex_unlock(&ClMut);
+ dList_remove(Clients, (void *)client);
_MSG("Closing Socket Handler\n");
- sock_handler_close(Client->sh);
- sock_handler_free(Client->sh);
- dFree(Client);
-}
-
-/*
- * Return the number of clients.
- */
-static int File_num_clients(void)
-{
- uint_t n;
-
- pthread_mutex_lock(&ClMut);
- n = dList_length(Clients);
- pthread_mutex_unlock(&ClMut);
-
- return n;
+ a_Dpip_dsh_close(client->sh);
+ a_Dpip_dsh_free(client->sh);
+ File_close(client->file_fd);
+ dFree(client->orig_url);
+ dFree(client->filename);
+ File_dillodir_free(client->d_dir);
+
+ dFree(client);
}
/*
* Serve this client.
- * (this function runs on its own thread)
*/
-static void *File_serve_client(void *data)
+static void File_serve_client(void *data, int f_write)
{
- char *dpip_tag, *cmd = NULL, *url = NULL, *path;
- ClientInfo *Client = data;
-
- /* Read the dpi command */
- dpip_tag = sock_handler_read(Client->sh);
- _MSG("dpip_tag={%s}\n", dpip_tag);
-
- if (dpip_tag) {
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- if (cmd) {
- if (strcmp(cmd, "DpiBye") == 0) {
- DPIBYE = 1;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *path;
+ ClientInfo *client = data;
+ int st;
+
+ while (1) {
+ _MSG("File_serve_client %p, flags=%d state=%d\n",
+ client, client->flags, client->state);
+ if (client->flags & (FILE_DONE | FILE_ERR))
+ break;
+ if (client->flags & FILE_READ) {
+ dpip_tag = a_Dpip_dsh_read_token(client->sh, 0);
+ _MSG("dpip_tag={%s}\n", dpip_tag);
+ if (!dpip_tag)
+ break;
+ }
+
+ if (client->flags & FILE_READ) {
+ if (!(client->flags & FILE_AUTH_OK)) {
+ /* Authenticate our client... */
+ st = a_Dpip_check_auth(dpip_tag);
+ _MSG("a_Dpip_check_auth returned %d\n", st);
+ client->flags |= (st == 1) ? FILE_AUTH_OK : FILE_ERR;
} else {
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- if (!url)
- MSG("file.dpi:: Failed to parse 'url'\n");
+ /* Get file request */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ path = File_normalize_path(url);
+ if (cmd) {
+ if (strcmp(cmd, "DpiBye") == 0) {
+ DPIBYE = 1;
+ MSG("(pid %d): Got DpiBye.\n", (int)getpid());
+ client->flags |= FILE_DONE;
+ } else if (url && strcmp(url, "dpi:/file/toggle") == 0) {
+ File_toggle_html_style(client);
+ } else if (path) {
+ File_get(client, path, url);
+ } else {
+ client->flags |= FILE_ERR;
+ MSG("ERROR: URL was %s\n", url);
+ }
+ }
+ dFree(path);
+ dFree(url);
+ dFree(cmd);
+ dFree(dpip_tag);
+ break;
}
+ dFree(dpip_tag);
+
+ } else if (f_write) {
+ /* send our answer */
+ if (client->state == st_err)
+ File_send_error_page(client);
+ else if (client->d_dir)
+ File_send_dir(client);
+ else
+ File_send_file(client);
+ break;
}
- }
- dFree(cmd);
- dFree(dpip_tag);
-
- if (!DPIBYE && url) {
- _MSG("url = '%s'\n", url);
-
- path = File_normalize_path(url);
- if (path) {
- _MSG("path = '%s'\n", path);
- File_get(Client, path, url);
- } else if (strcmp(url, "dpi:/file/toggle") == 0) {
- File_toggle_html_style(Client);
- } else {
- MSG("ERROR: URL path was %s\n", url);
- }
- dFree(path);
- }
- dFree(url);
+ } /*while*/
- /* flag the the transfer finished */
- Client->done = 1;
-
- return NULL;
+ client->flags |= (client->sh->status & DPIP_ERROR) ? FILE_ERR : 0;
+ client->flags |= (client->sh->status & DPIP_EOF) ? FILE_DONE : 0;
}
/*
* Serve the client queue.
- * (this function runs on its own thread)
*/
-static void *File_serve_clients(void *client)
+static void File_serve_clients()
{
- /* switch to detached state */
- pthread_detach(pthread_self());
-
- while (File_num_clients()) {
- client = File_get_client_n((uint_t)0);
- File_serve_client(client);
- File_remove_client_n((uint_t)0);
+ int i, f_read, f_write;
+ ClientInfo *client;
+
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ f_read = FD_ISSET(client->sh->fd_in, &read_set);
+ f_write = FD_ISSET(client->sh->fd_out, &write_set);
+ if (!f_read && !f_write)
+ continue;
+ File_serve_client(client, f_write);
+ if (client->flags & (FILE_DONE | FILE_ERR)) {
+ File_remove_client(client);
+ --i;
+ }
}
- ThreadRunning = 0;
-
- return NULL;
}
/* --------------------------------------------------------------------------*/
/*
- * Check a fd for activity, with a max timeout.
+ * Check the fd sets for activity, with a max timeout.
* return value: 0 if timeout, 1 if input available, -1 if error.
*/
-static int File_check_fd(int filedes, unsigned int seconds)
+static int File_check_fds(uint_t seconds)
{
- int st;
- fd_set set;
- struct timeval timeout;
-
- /* Initialize the file descriptor set. */
- FD_ZERO (&set);
- FD_SET (filedes, &set);
-
- /* Initialize the timeout data structure. */
- timeout.tv_sec = seconds;
- timeout.tv_usec = 0;
+ int i, st;
+ ClientInfo *client;
+ struct timeval timeout;
+
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ if (client->flags & FILE_READ)
+ FD_SET (client->sh->fd_in, &read_set);
+ if (client->flags & FILE_WRITE)
+ FD_SET (client->sh->fd_out, &write_set);
+ }
+ _MSG("Watching %d fds\n", dList_length(Clients) + 1);
- do {
- st = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
- } while (st == -1 && errno == EINTR);
+ /* Initialize the timeout data structure. */
+ timeout.tv_sec = seconds;
+ timeout.tv_usec = 0;
- return st;
+ do {
+ st = select(FD_SETSIZE, &read_set, &write_set, NULL, &timeout);
+ } while (st == -1 && errno == EINTR);
+/*
+ MSG_RAW(" (%d%s%s)", STDIN_FILENO,
+ FD_ISSET(STDIN_FILENO, &read_set) ? "R" : "",
+ FD_ISSET(STDIN_FILENO, &write_set) ? "W" : "");
+ for (i = 0; (client = dList_nth_data(Clients, i)); ++i) {
+ MSG_RAW(" (%d%s%s)", client->sh->fd_in,
+ FD_ISSET(client->sh->fd_in, &read_set) ? "R" : "",
+ FD_ISSET(client->sh->fd_out, &write_set) ? "W" : "");
+ }
+ MSG_RAW("\n");
+*/
+ return st;
}
int main(void)
{
- ClientInfo *NewClient;
- struct sockaddr_un spun;
- int temp_sock_descriptor;
- socklen_t address_size;
- int c_st, st = 1;
- uint_t i;
+ struct sockaddr_in sin;
+ socklen_t sin_sz;
+ int sock_fd, c_st, st = 1;
/* Arrange the cleanup function for abnormal terminations */
if (signal (SIGINT, termination_handler) == SIG_IGN)
@@ -930,57 +1057,57 @@ int main(void)
if (signal (SIGTERM, termination_handler) == SIG_IGN)
signal (SIGTERM, SIG_IGN);
- MSG("(v.1) accepting connections...\n");
+ MSG("(v.2) accepting connections...\n");
+ //sleep(20);
- /* initialize mutex */
- pthread_mutex_init(&ClMut, NULL);
+ /* initialize observed file descriptors */
+ FD_ZERO (&read_set);
+ FD_ZERO (&write_set);
+ FD_SET (STDIN_FILENO, &read_set);
+
+ /* Set STDIN socket nonblocking (to ensure accept() never blocks) */
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK | fcntl(STDIN_FILENO, F_GETFL));
/* initialize Clients list */
Clients = dList_new(512);
/* some OSes may need this... */
- address_size = sizeof(struct sockaddr_un);
+ sin_sz = sizeof(sin);
/* start the service loop */
while (!DPIBYE) {
- /* wait for a connection */
+ /* wait for activity */
do {
- c_st = File_check_fd(STDIN_FILENO, 1);
+ c_st = File_check_fds(10);
} while (c_st == 0 && !DPIBYE);
if (c_st < 0) {
- perror("[select]");
+ MSG(" select() %s\n", dStrerror(errno));
break;
}
if (DPIBYE)
break;
- temp_sock_descriptor =
- accept(STDIN_FILENO, (struct sockaddr *)&spun, &address_size);
-
- if (temp_sock_descriptor == -1) {
- perror("[accept]");
- break;
- }
-
- /* Create and initialize a new client */
- NewClient = File_add_client(temp_sock_descriptor);
-
- if (!ThreadRunning) {
- ThreadRunning = 1;
- /* Serve the client from a thread (avoids deadlocks) */
- if (pthread_create(&NewClient->thrID, NULL,
- File_serve_clients, NewClient) != 0) {
- perror("[pthread_create]");
- ThreadRunning = 0;
+ if (FD_ISSET(STDIN_FILENO, &read_set)) {
+ /* accept the incoming connection */
+ do {
+ sock_fd = accept(STDIN_FILENO, (struct sockaddr *)&sin, &sin_sz);
+ } while (sock_fd < 0 && errno == EINTR);
+ if (sock_fd == -1) {
+ if (errno == EAGAIN)
+ continue;
+ MSG(" accept() %s\n", dStrerror(errno));
break;
+ } else {
+ _MSG(" accept() fd=%d\n", sock_fd);
+ /* Set nonblocking */
+ fcntl(sock_fd, F_SETFL, O_NONBLOCK | fcntl(sock_fd, F_GETFL));
+ /* Create and initialize a new client */
+ File_add_client(sock_fd);
}
+ continue;
}
- }
- /* TODO: handle a running thread better. */
- for (i = 0; i < 5 && ThreadRunning; ++i) {
- MSG("sleep i=%u", i);
- sleep(i);
+ File_serve_clients();
}
if (DPIBYE)
diff --git a/dpi/ftp.c b/dpi/ftp.c
index 139444c9..6b1e2f31 100644
--- a/dpi/ftp.c
+++ b/dpi/ftp.c
@@ -56,7 +56,7 @@
/*
* Global variables
*/
-static SockHandler *sh = NULL;
+static Dsh *sh = NULL;
static char **dl_argv = NULL;
/*---------------------------------------------------------------------------*/
@@ -97,7 +97,7 @@ static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
size_t i, non_ascci;
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -129,7 +129,7 @@ static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
Size = MIN (Size, 256);
for (i = 0; i < Size; i++) {
ch = (uchar_t) p[i];
- if ((ch < 32 || ch > 126) && !isspace(ch))
+ if ((ch < 32 || ch > 126) && !dIsspace(ch))
++non_ascci;
}
if (Size == 256) {
@@ -171,23 +171,24 @@ static void make_wget_argv(char *url)
/*
* Fork, exec command, get its output and send via stdout.
- * Return: Number of bytes transfered.
+ * Return: Number of bytes transfered, -1 if file-not_found, -2 if aborted.
*/
static int try_ftp_transfer(char *url)
{
-#define MinSZ 256
+#define MIN_SZ 256
+#define READ_SZ 16*1024
ssize_t n;
- int nb, has_mime_type, has_html_header;
+ int nb, has_mime_type, has_html_header, no_such_file, offer_download;
const char *mime_type = "application/octet-stream";
- char buf[4096], *d_cmd;
- Dstr *dbuf = dStr_sized_new(4096);
+ char buf[READ_SZ], *d_cmd;
+ Dstr *dbuf = dStr_sized_new(READ_SZ);
pid_t ch_pid;
int aborted = 0;
int DataPipe[2];
if (pipe(DataPipe) < 0) {
- MSG("pipe, %s\n", strerror(errno));
+ MSG("pipe, %s\n", dStrerror(errno));
return 0;
}
@@ -214,18 +215,24 @@ static int try_ftp_transfer(char *url)
nb = 0;
has_mime_type = 0;
has_html_header = 0;
+ no_such_file = 0;
+ offer_download = 0;
do {
- while ((n = read(DataPipe[0], buf, 4096)) < 0 && errno == EINTR);
+ while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
if (n > 0) {
dStr_append_l(dbuf, buf, n);
- if (!has_mime_type && dbuf->len < MinSZ)
+ if (!has_mime_type && dbuf->len < MIN_SZ)
continue;
} else if (n < 0)
break;
if (!has_mime_type) {
- if (dbuf->len > 0)
- a_Misc_get_content_type_from_data2(dbuf->str,dbuf->len,&mime_type);
+ if (dbuf->len == 0) {
+ /* When the file doesn't exist, the transfer size is zero */
+ no_such_file = 1;
+ break;
+ }
+ a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
has_mime_type = 1;
if (strcmp(mime_type, "application/octet-stream") == 0) {
@@ -233,31 +240,33 @@ static int try_ftp_transfer(char *url)
kill(ch_pid, SIGTERM);
/* The "application/octet-stream" MIME type will be sent and
* Dillo will offer a download dialog */
+ offer_download = 1;
aborted = 1;
}
}
- if (!has_html_header && dbuf->len) {
+ if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
- sock_handler_write_str(sh, 0, "Content-type: ");
- sock_handler_write_str(sh, 0, mime_type);
- sock_handler_write_str(sh, 1, "\n\n");
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
+ a_Dpip_dsh_write_str(sh, 0, mime_type);
+ a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
has_html_header = 1;
}
if (!aborted && dbuf->len) {
- sock_handler_write(sh, 0, dbuf->str, dbuf->len);
+ a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
nb += dbuf->len;
dStr_truncate(dbuf, 0);
}
} while (n > 0 && !aborted);
- return nb;
+ dStr_free(dbuf, 1);
+ return (no_such_file ? -1 : (aborted ? -2 : nb));
}
/*
@@ -265,47 +274,66 @@ static int try_ftp_transfer(char *url)
*/
int main(int argc, char **argv)
{
+ const char *err_msg = "404 Not Found\nNo such file or directory";
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
- int nb;
+ int st, rc;
char *p, *d_cmd;
- /* Debugging with a command line argument */
- if (argc == 2)
- dpip_tag = dStrdup(argv[1]);
+ /* wget may need to write a temporary file... */
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
/* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
-
- /* wget may need to write a temporary file... */
- chdir("/tmp");
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
- /* Read the dpi command from STDIN */
- if (!dpip_tag)
- dpip_tag = sock_handler_read(sh);
+ if (argc == 2) {
+ /* Debugging with a command line argument */
+ dpip_tag = dStrdup(argv[1]);
+ } else {
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
+ /* Read the dpi command from STDIN */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ }
MSG("tag=[%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
}
- if ((nb = try_ftp_transfer(url)) == 0) {
+ if ((st = try_ftp_transfer(url)) == -1) {
/* Transfer failed, the requested file may not exist or be a symlink
* to a directory. Try again... */
if ((p = strrchr(url, '/')) && p[1]) {
url2 = dStrconcat(url, "/", NULL);
- nb = try_ftp_transfer(url2);
+ st = try_ftp_transfer(url2);
}
}
- if (nb == 0) {
+ if (st == -1) {
/* The transfer failed, let dillo know... */
- d_cmd = a_Dpip_build_cmd("cmd=%s to_cmd=%s msg=%s",
- "answer", "open_url", "not a directory");
- sock_handler_write_str(sh, 1, d_cmd);
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
+ a_Dpip_dsh_printf(sh, 1,
+ "HTTP/1.1 404 Not Found\r\n"
+ "Content-Type: text/plain\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%s",
+ strlen(err_msg), err_msg);
}
dFree(cmd);
@@ -314,8 +342,8 @@ int main(int argc, char **argv)
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/hello.c b/dpi/hello.c
index 250a4a44..4643efc5 100644
--- a/dpi/hello.c
+++ b/dpi/hello.c
@@ -36,7 +36,7 @@
int main(void)
{
FILE *in_stream;
- SockHandler *sh;
+ Dsh *sh;
char *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
char *esc_tag, *d_cmd;
size_t n;
@@ -45,21 +45,46 @@ int main(void)
char *choice[] = {"Window was closed", "Yes", "No",
"Could be", "It's OK", "Cancel"};
/* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
- int choice_num;
+ int choice_num = -1;
MSG("starting...\n");
+ /* sleep(20) */
+
+ /* Initialize the SockHandler.
+ * This means we'll use stdin for input and stdout for output.
+ * In case of a server dpi, we'd use a socket and pass its file descriptor
+ * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
+ * (Note: by now the last parameter is not used) */
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
+
+ /* Authenticate our client...
+ * As we're using Internet domain sockets, DPIP checks whether the client
+ * runs with the user's ID, by means of a shared secret. The DPIP API does
+ * the work for us. */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
- /* Initialize the SockHandler */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
-
- /* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ /* Read the dpi command from STDIN
+ * Now we're past the authentication phase, let's see what's dillo
+ * asking from us. a_Dpip_dsh_read_token() will block and return
+ * a full dpip token or null on error (it's commented in dpip.c) */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
+ /* Now that we have the dpip_tag, let's isolate the command and url */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
/*-- Dialog part */
+/* This is the dialog window. This is an example of interaction with
+ * the user. If you're starting to understand dpis, comment this out
+ * by switching to "#if 0" and the dialog will be disabled. */
+#if 1
{
char *dpip_tag2, *dialog_msg;
@@ -69,15 +94,15 @@ int main(void)
"cmd=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
"dialog", "Do you want to see the hello page?",
choice[1], choice[2], choice[3], choice[4], choice[5]);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Get the answer */
- dpip_tag2 = sock_handler_read(sh);
+ dpip_tag2 = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag2);
/* Get "msg" value */
- dialog_msg = a_Dpip_get_attr(dpip_tag2, strlen(dpip_tag2), "msg");
+ dialog_msg = a_Dpip_get_attr(dpip_tag2, "msg");
choice_num = 0;
if (dialog_msg)
choice_num = *dialog_msg - '0';
@@ -85,14 +110,16 @@ int main(void)
dFree(dialog_msg);
dFree(dpip_tag2);
}
+#endif
/*-- EOD part */
- /* Start sending our answer */
+ /* Start sending our answer.
+ * (You can read the comments for DPIP API functions in dpip/dpip.c) */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 0, d_cmd);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"Content-type: text/html\n\n"
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html>\n"
@@ -100,17 +127,17 @@ int main(void)
"<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
/* Show the choice received with the dialog */
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"<hr>\n"
"<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
"<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
"<em>Answer received:</em> <b>%s</b></big> </table>\n"
"<hr>\n",
- choice[choice_num]);
+ choice_num < 0 ? "There was NO dialog!" : choice[choice_num]);
/* Show the dpip tag we received */
esc_tag = Escape_html_str(dpip_tag);
- sock_handler_printf(sh, 0,
+ a_Dpip_dsh_printf(sh, 0,
"<h3>dpip tag received:</h3>\n"
"<pre>\n%s</pre>\n"
"<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
@@ -119,7 +146,9 @@ int main(void)
/* Now something more interesting,
- * fork a command and show its feedback */
+ * fork a command and show its feedback.
+ * (An example of generating dynamic content with an external
+ * program). */
if (cmd && url) {
child_cmd = dStrdup("date -R");
MSG("[%s]\n", child_cmd);
@@ -130,15 +159,15 @@ int main(void)
return EXIT_FAILURE;
}
- sock_handler_printf(sh, 0, "<h3>date:</h3>\n");
- sock_handler_printf(sh, 0, "<pre>\n");
+ a_Dpip_dsh_write_str(sh, 0, "<h3>date:</h3>\n");
+ a_Dpip_dsh_write_str(sh, 0, "<pre>\n");
/* Read/Write */
while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
- sock_handler_write(sh, 0, buf, n);
+ a_Dpip_dsh_write(sh, 0, buf, n);
}
- sock_handler_printf(sh, 0, "</pre>\n");
+ a_Dpip_dsh_write_str(sh, 0, "</pre>\n");
if ((ret = pclose(in_stream)) != 0)
MSG("popen: [%d]\n", ret);
@@ -146,15 +175,15 @@ int main(void)
dFree(child_cmd);
}
- sock_handler_printf(sh, 1, "</body></html>\n");
+ a_Dpip_dsh_write_str(sh, 1, "</body></html>\n");
dFree(cmd);
dFree(url);
dFree(dpip_tag);
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
return 0;
}
diff --git a/dpi/https.c b/dpi/https.c
index 00ceb0c1..3f474b81 100644
--- a/dpi/https.c
+++ b/dpi/https.c
@@ -25,7 +25,7 @@
* the https dillo plugin with the OpenSSL project's "OpenSSL"
* library, and distribute the linked executables, without including
* the source code for OpenSSL in the source distribution. You must
- * obey the GNU General Public License, version 2, in all respects
+ * obey the GNU General Public License, version 3, in all respects
* for all of the code used other than "OpenSSL".
*
*/
@@ -64,12 +64,15 @@
/*
* Debugging macros
*/
+#define SILENT 1
#define _MSG(...)
-#define MSG(...) printf("[https dpi]: " __VA_ARGS__)
+#if SILENT
+ #define MSG(...)
+#else
+ #define MSG(...) fprintf(stderr, "[https dpi]: " __VA_ARGS__)
+#endif
-#define ENABLE_SSL
-#undef ENABLE_SSL
#ifdef ENABLE_SSL
#include <openssl/ssl.h>
@@ -88,7 +91,7 @@ static int save_certificate_home(X509 * cert);
* Global variables
*/
static char *root_url = NULL; /*Holds the URL we are connecting to*/
-static SockHandler *sh;
+static Dsh *sh;
#ifdef ENABLE_SSL
@@ -104,8 +107,8 @@ static int dialog_get_answer_number(void)
char *dpip_tag, *response;
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
- response = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "msg");
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ response = a_Dpip_get_attr(dpip_tag, "msg");
response_number = (response) ? strtol (response, NULL, 10) : -1;
dFree(dpip_tag);
dFree(response);
@@ -126,9 +129,10 @@ static void yes_ssl_support(void)
SSL_CTX * ssl_context = NULL;
SSL * ssl_connection = NULL;
- char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL;
+ char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *http_query = NULL,
+ *proxy_url = NULL, *proxy_connect = NULL;
char buf[4096];
- int retval = 0;
+ int ret = 0;
int network_socket = -1;
@@ -189,10 +193,13 @@ static void yes_ssl_support(void)
SSL_set_verify(ssl_connection, SSL_VERIFY_NONE, 0);
/*Get the network address and command to be used*/
- dpip_tag = sock_handler_read(sh);
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ proxy_url = a_Dpip_get_attr(dpip_tag, "proxy_url");
+ proxy_connect =
+ a_Dpip_get_attr(dpip_tag, "proxy_connect");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ http_query = a_Dpip_get_attr(dpip_tag, "query");
if (cmd == NULL || url == NULL || http_query == NULL){
MSG("***Value of cmd, url or http_query is NULL"
@@ -202,13 +209,66 @@ static void yes_ssl_support(void)
}
if (exit_error == 0){
- network_socket = get_network_connection(url);
+ char *connect_url = proxy_url ? proxy_url : url;
+
+ network_socket = get_network_connection(connect_url);
if (network_socket<0){
MSG("Network socket create error\n");
exit_error = 1;
}
}
+ if (exit_error == 0 && proxy_connect != NULL) {
+ ssize_t St;
+ const char *p = proxy_connect;
+ int writelen = strlen(proxy_connect);
+
+ while (writelen > 0) {
+ St = write(network_socket, p, writelen);
+ if (St < 0) {
+ /* Error */
+ if (errno != EINTR) {
+ MSG("Error writing to proxy.\n");
+ exit_error = 1;
+ break;
+ }
+ } else {
+ p += St;
+ writelen -= St;
+ }
+ }
+ if (exit_error == 0) {
+ const size_t buflen = 200;
+ char buf[buflen];
+ Dstr *reply = dStr_new("");
+
+ while (1) {
+ St = read(network_socket, buf, buflen);
+ if (St > 0) {
+ dStr_append_l(reply, buf, St);
+ if (strstr(reply->str, "\r\n\r\n")) {
+ /* have whole reply header */
+ if (reply->len >= 12 && reply->str[9] == '2') {
+ /* e.g. "HTTP/1.1 200 Connection established[...]" */
+ MSG("CONNECT through proxy succeeded.\n");
+ } else {
+ /* TODO: send reply body to dillo */
+ exit_error = 1;
+ MSG("CONNECT through proxy failed.\n");
+ }
+ break;
+ }
+ } else if (St < 0) {
+ if (errno != EINTR) {
+ exit_error = 1;
+ MSG("Error reading from proxy.\n");
+ break;
+ }
+ }
+ }
+ dStr_free(reply, 1);
+ }
+ }
if (exit_error == 0){
/* Configure SSL to use network file descriptor */
@@ -244,14 +304,14 @@ static void yes_ssl_support(void)
/*Send dpi command*/
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/*Send remaining data*/
- while ((retval = SSL_read(ssl_connection, buf, 4096)) > 0 ){
+ while ((ret = SSL_read(ssl_connection, buf, 4096)) > 0 ){
/* flush is good for dialup speed */
- sock_handler_write(sh, 1, buf, (size_t)retval);
+ a_Dpip_dsh_write(sh, 1, buf, (size_t)ret);
}
}
@@ -260,6 +320,8 @@ static void yes_ssl_support(void)
dFree(cmd);
dFree(url);
dFree(http_query);
+ dFree(proxy_url);
+ dFree(proxy_connect);
if (network_socket != -1){
close(network_socket);
@@ -288,12 +350,15 @@ static int get_network_connection(char * url)
int s;
int url_offset = 0;
int portnum = 443;
- unsigned int url_look_up_length = 0;
+ uint_t url_look_up_length = 0;
char * url_look_up = NULL;
/*Determine how much of url we chop off as unneeded*/
if (dStrncasecmp(url, "https://", 8) == 0){
url_offset = 8;
+ } else if (dStrncasecmp(url, "http://", 7) == 0) {
+ url_offset = 7;
+ portnum = 80;
}
/*Find end of URL*/
@@ -305,7 +370,7 @@ static int get_network_connection(char * url)
/*Check for port number*/
if (strchr(url+url_offset, ':') ==
(url + url_offset + url_look_up_length)){
- portnum = atoi(url + url_offset + url_look_up_length + 1);
+ portnum = strtol(url + url_offset + url_look_up_length + 1, NULL, 10);
}
} else {
url_look_up = url + url_offset;
@@ -314,7 +379,7 @@ static int get_network_connection(char * url)
root_url = dStrdup(url_look_up);
hp=gethostbyname(url_look_up);
- /*url_look_uip no longer needed, so free if neccessary*/
+ /*url_look_uip no longer needed, so free if necessary*/
if (url_look_up_length != 0){
dFree(url_look_up);
}
@@ -349,9 +414,9 @@ static int get_network_connection(char * url)
static int handle_certificate_problem(SSL * ssl_connection)
{
int response_number;
- int retval = -1;
+ int ret = -1;
long st;
- char *cn, *cn_end;
+ char *cn;
char buf[4096], *d_cmd, *msg;
X509 * remote_cert;
@@ -366,7 +431,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
"This site CAN NOT be trusted. Sending data is NOT SAFE.\n"
"What do I do?",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/*Read the user's response*/
@@ -374,7 +439,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
/*Abort on anything but "Continue"*/
if (response_number == 1){
- retval = 0;
+ ret = 0;
}
} else {
@@ -382,36 +447,37 @@ static int handle_certificate_problem(SSL * ssl_connection)
st = SSL_get_verify_result(ssl_connection);
switch (st) {
case X509_V_OK: /*Everything is Kosher*/
- retval = 0;
+ ret = 0;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
/*Either self signed and untrusted*/
/*Extract CN from certificate name information*/
- cn = strstr(remote_cert->name, "/CN=") + 4;
- if (cn == NULL)
- break;
-
- if ((cn_end = strstr(cn, "/")) == NULL )
- cn_end = cn + strlen(cn);
+ if ((cn = strstr(remote_cert->name, "/CN=")) == NULL) {
+ strcpy(buf, "(no CN given)");
+ } else {
+ char *cn_end;
- strncpy(buf, cn, (size_t) (cn_end - cn));
+ cn += 4;
- /*Add terminating NULL*/
- buf[cn_end - cn] = 0;
+ if ((cn_end = strstr(cn, "/")) == NULL )
+ cn_end = cn + strlen(cn);
+ strncpy(buf, cn, (size_t) (cn_end - cn));
+ buf[cn_end - cn] = '\0';
+ }
msg = dStrconcat("The remote certificate is self-signed and "
"untrusted.\nFor address: ", buf, NULL);
d_cmd = a_Dpip_build_cmd(
"cmd=%s msg=%s alt1=%s alt2=%s alt3=%s",
- "dialog", msg, "Continue", "Cancel", "Trust Certificate");
- sock_handler_write_str(sh, 1, d_cmd);
+ "dialog", msg, "Continue", "Cancel", "Save Certificate");
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
dFree(msg);
response_number = dialog_get_answer_number();
switch (response_number){
case 1:
- retval = 0;
+ ret = 0;
break;
case 2:
break;
@@ -420,7 +486,7 @@ static int handle_certificate_problem(SSL * ssl_connection)
/*Potential security problems because we are writing
*to the filesystem*/
save_certificate_home(remote_cert);
- retval = 1;
+ ret = 1;
break;
default:
break;
@@ -434,12 +500,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"The issuer for the remote certificate cannot be found\n"
"The authenticity of the remote certificate cannot be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
@@ -453,12 +519,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"The remote certificate signature could not be read\n"
"or is invalid and should not be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
@@ -472,12 +538,12 @@ static int handle_certificate_problem(SSL * ssl_connection)
"presented has a starting validity after today's date\n"
"You should be cautious about using this site",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
@@ -489,11 +555,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"wasn't designed to last this long. You should avoid \n"
"this site.",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
@@ -508,11 +574,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"making it impossible to determine if the certificate\n"
"is valid. You should not trust this certificate.",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_INVALID_CA:
@@ -528,11 +594,11 @@ static int handle_certificate_problem(SSL * ssl_connection)
"with the remote system. The connection should not\n"
"be trusted",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
if (response_number == 1) {
- retval = 0;
+ ret = 0;
}
break;
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
@@ -543,9 +609,9 @@ static int handle_certificate_problem(SSL * ssl_connection)
"dialog",
"Some of the information presented by the remote system\n"
"does not match other information presented\n"
- "This may be an attempt to evesdrop on communications",
+ "This may be an attempt to eavesdrop on communications",
"Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
default: /*Need to add more options later*/
snprintf(buf, 80,
@@ -553,19 +619,19 @@ static int handle_certificate_problem(SSL * ssl_connection)
d_cmd = a_Dpip_build_cmd(
"cmd=%s msg=%s alt1=%s alt2=%s",
"dialog", buf, "Continue", "Cancel");
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
response_number = dialog_get_answer_number();
/*abort on anything but "Continue"*/
if (response_number == 1){
- retval = 0;
+ ret = 0;
}
}
X509_free(remote_cert);
remote_cert = 0;
}
- return retval;
+ return ret;
}
/*
@@ -577,8 +643,8 @@ static int save_certificate_home(X509 * cert)
char buf[4096];
FILE * fp = NULL;
- unsigned int i = 0;
- int retval = 1;
+ uint_t i = 0;
+ int ret = 1;
/*Attempt to create .dillo/certs blindly - check later*/
snprintf(buf,4096,"%s/.dillo/", dGethomedir());
@@ -586,7 +652,7 @@ static int save_certificate_home(X509 * cert)
snprintf(buf,4096,"%s/.dillo/certs/", dGethomedir());
mkdir(buf, 01777);
- do{
+ do {
snprintf(buf, 4096, "%s/.dillo/certs/%lx.%u",
dGethomedir(), X509_subject_name_hash(cert), i);
@@ -601,7 +667,7 @@ static int save_certificate_home(X509 * cert)
PEM_write_X509(fp, cert);
fclose(fp);
MSG("Wrote certificate\n");
- retval = 0;
+ ret = 0;
break;
}
} else {
@@ -609,9 +675,9 @@ static int save_certificate_home(X509 * cert)
}
i++;
/*Don't loop too many times - just give up*/
- } while( i < 1024 );
+ } while (i < 1024);
- return retval;
+ return ret;
}
@@ -629,14 +695,14 @@ static void no_ssl_support(void)
char *d_cmd;
/* Read the dpi command from STDIN */
- dpip_tag = sock_handler_read(sh);
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("{In https.filter.dpi}\n");
MSG("no_ssl_support version\n");
- cmd = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cmd");
- url = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "url");
- http_query = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "query");
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+ http_query = a_Dpip_get_attr(dpip_tag, "query");
MSG("{ cmd: %s}\n", cmd);
MSG("{ url: %s}\n", url);
@@ -645,33 +711,35 @@ static void no_ssl_support(void)
MSG("{ sending dpip cmd...}\n");
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
- sock_handler_write_str(sh, 1, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
MSG("{ dpip cmd sent.}\n");
MSG("{ sending HTML...}\n");
- sock_handler_printf(sh, 1,
+ a_Dpip_dsh_printf(sh, 1,
"Content-type: text/html\n\n"
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
- "<html><body><pre>\n"
- "<b>Hi!\n\n"
- " This is the https dpi that just got a request to send\n"
- " the following HTTP query:\n{</b>\n"
- "<code>%s</code>\n"
- "<b>}</b>\n\n"
- " <b>*** Dillo's prototype plugin for https support"
- " is disabled now ***</b>\n\n"
- " If you want to test this <b>alpha</b> support code, just remove\n"
- " line 72 from dpi/https.c, recompile and reinstall.\n\n"
- " (beware that this https support is very limited now)\n\n"
+ "<html><head><title>SSL support is disabled</title></head>\n"
+ "<body>\n"
+ "<p>\n"
+ " The https dpi was unable to send\n"
+ " the following HTTP query:\n"
+ " <blockquote><pre>%s</pre></blockquote>\n"
+ " because Dillo's prototype plugin for https support"
+ " is disabled.\n\n"
+ "<p>\n"
+ " If you want to test this <b>alpha</b> support code,\n"
+ " just reconfigure with <code>--enable-ssl</code>,\n"
+ " recompile and reinstall.\n\n"
+ " (Beware that this https support is very limited now)\n\n"
" To use https and SSL, you must have \n"
" the OpenSSL development libraries installed. Check your\n"
" O/S distribution provider, or check out\n"
- " <a href=\"http://www.openssl.org\">www.openssl.org</a>\n\n"
- " --\n"
- "</pre></body></html>\n",
+ " <a href=\"http://www.openssl.org\">www.openssl.org</a>.\n\n"
+ "</p>\n\n"
+ "</body></html>\n",
http_query
);
MSG("{ HTML content sent.}\n");
@@ -691,8 +759,19 @@ static void no_ssl_support(void)
/*---------------------------------------------------------------------------*/
int main(void)
{
+ char *dpip_tag;
+
/* Initialize the SockHandler for this filter dpi */
- sh = sock_handler_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
+
+ /* Authenticate our client... */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
#ifdef ENABLE_SSL
yes_ssl_support();
@@ -701,8 +780,8 @@ int main(void)
#endif
/* Finish the SockHandler */
- sock_handler_close(sh);
- sock_handler_free(sh);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
dFree(root_url);
diff --git a/dpi/vsource.c b/dpi/vsource.c
new file mode 100644
index 00000000..113aaa99
--- /dev/null
+++ b/dpi/vsource.c
@@ -0,0 +1,239 @@
+/*
+ * Dpi for "View source".
+ *
+ * This server is an example. Play with it and modify to your taste.
+ *
+ * Copyright 2010 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include "../dpip/dpip.h"
+#include "dpiutil.h"
+
+/*
+ * Debugging macros
+ */
+#define _MSG(...)
+#define MSG(...) printf("[vsource dpi]: " __VA_ARGS__)
+
+/*---------------------------------------------------------------------------*/
+
+const char *DOCTYPE=
+ "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
+
+
+void send_dpip_tag(Dsh *sh, char *dpip_tag)
+{
+ a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
+ a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
+ a_Dpip_dsh_write_str(sh, 1, "\n\n");
+}
+
+/*
+ * Send source as plain text
+ */
+void send_plain_text(Dsh *sh, int data_size)
+{
+ int bytes_read = 0;
+ char *src_str;
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ a_Dpip_dsh_write_str(sh, 1, src_str);
+ dFree(src_str);
+ }
+}
+
+/*
+ * Send source as plain text with line numbers
+ */
+void send_numbered_text(Dsh *sh, int data_size)
+{
+ int bytes_read = 0, line = 1;
+ char *p, *q, *src_str, line_str[32];
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ p = q = src_str;
+
+ while (*p) {
+ snprintf(line_str, 32, "%2d: ", line);
+ a_Dpip_dsh_write_str(sh, 0, line_str);
+ if ((p = strchr(q, '\n'))) {
+ a_Dpip_dsh_write(sh, 0, q, p - q + 1);
+ if (p[1] == '\r')
+ ++p;
+ ++line;
+ } else {
+ a_Dpip_dsh_write_str(sh, 1, q);
+ break;
+ }
+ q = ++p;
+ }
+ dFree(src_str);
+ }
+}
+
+/*
+ * Send source as html text with line numbers
+ */
+void send_html_text(Dsh *sh, const char *url, int data_size)
+{
+ int bytes_read = 0, old_line = 0, line = 1;
+ char *p, *q, *src_str, line_str[128];
+
+ if (strncmp(url, "dpi:/vsource/:", 14) == 0)
+ url += 14;
+
+ /* Send HTTP header for plain text MIME type */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
+
+ a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
+ a_Dpip_dsh_printf(sh, 0,
+ "\n"
+ "<html><head>\n"
+ "<title>Source for %s</title>\n"
+ "<style type=\"text/css\">PRE {white-space: pre-wrap}\n"
+ "</style>\n"
+ "</head>\n"
+ "<body id=\"dillo_vs\">\n<table cellpadding='0'>\n", url);
+
+ while (bytes_read < data_size &&
+ (src_str = a_Dpip_dsh_read_token(sh, 1))) {
+ bytes_read += strlen(src_str);
+ p = q = src_str;
+
+ while (*p) {
+ if (line > old_line) {
+ snprintf(line_str, 128,
+ "%s<tr><td bgcolor='%s'>%d%s<td><pre>",
+ (line > 1) ? "</pre>" : "",
+ (line & 1) ? "#B87333" : "#DD7F32", line,
+ (line == 1 || (line % 10) == 0) ? "&nbsp;&nbsp;" : "");
+ a_Dpip_dsh_write_str(sh, 0, line_str);
+ old_line = line;
+ }
+ if ((p = strpbrk(q, "\r\n<&"))) {
+ if (*p == '\r' || *p == '\n') {
+ a_Dpip_dsh_write(sh, 0, q, p - q + 1);
+ if (*p == '\r' && p[1] == '\n')
+ p++;
+ ++line;
+ } else {
+ a_Dpip_dsh_write(sh, 0, q, p - q);
+ a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "&lt;" : "&amp;");
+ }
+ } else {
+ a_Dpip_dsh_write_str(sh, 1, q);
+ break;
+ }
+ q = ++p;
+ }
+ dFree(src_str);
+ }
+
+ if (data_size > 0)
+ a_Dpip_dsh_write_str(sh, 0, "</pre>");
+ a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
+}
+
+/*
+ *
+ */
+int main(void)
+{
+ Dsh *sh;
+ int data_size;
+ char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
+ char *d_cmd;
+
+ _MSG("starting...\n");
+ //sleep(20);
+
+ /* Initialize the SockHandler.
+ * This means we'll use stdin for input and stdout for output.
+ * In case of a server dpi, we'd use a socket and pass its file descriptor
+ * twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
+ * (Note: by now the last parameter is not used) */
+ sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
+
+ /* Authenticate our client...
+ * As we're using Internet domain sockets, DPIP checks whether the client
+ * runs with the user's ID, by means of a shared secret. The DPIP API does
+ * the work for us. */
+ if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
+ a_Dpip_check_auth(dpip_tag) < 0) {
+ MSG("can't authenticate request: %s\n", dStrerror(errno));
+ a_Dpip_dsh_close(sh);
+ return 1;
+ }
+ dFree(dpip_tag);
+
+ /* Read the dpi command from STDIN
+ * Now we're past the authentication phase, let's see what's dillo
+ * asking from us. a_Dpip_dsh_read_token() will block and return
+ * a full dpip token or null on error (it's commented in dpip.c) */
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ MSG("tag = [%s]\n", dpip_tag);
+
+ /* Now that we have the dpip_tag, let's isolate the command and url */
+ cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+ url = a_Dpip_get_attr(dpip_tag, "url");
+
+ /* Start sending our answer.
+ * (You can read the comments for DPIP API functions in dpip/dpip.c) */
+ d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+ a_Dpip_dsh_write_str(sh, 0, d_cmd);
+ dFree(d_cmd);
+ dFree(dpip_tag);
+
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+ cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
+ if (cmd2) {
+ if (strcmp(cmd2, "start_send_page") == 0 &&
+ (size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
+ data_size = strtol(size_str, NULL, 10);
+ /* Choose your flavour */
+ //send_plain_text(sh, data_size);
+ //send_numbered_text(sh, data_size);
+ send_html_text(sh, url, data_size);
+ } else if (strcmp(cmd2, "DpiError") == 0) {
+ /* Dillo detected an error (other failures just close the socket) */
+ a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
+ a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
+ "ERROR: Page not cached.\n");
+ }
+ dFree(cmd2);
+ }
+
+ dFree(cmd);
+ dFree(url);
+ dFree(size_str);
+ dFree(dpip_tag);
+
+ /* Finish the SockHandler */
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
+
+ return 0;
+}
+
diff --git a/dpid/Makefile.am b/dpid/Makefile.am
index 938a6244..2b81a98a 100644
--- a/dpid/Makefile.am
+++ b/dpid/Makefile.am
@@ -1,31 +1,29 @@
AM_CPPFLAGS=-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
-bin_PROGRAMS = dpid
+bin_PROGRAMS = dpid dpidc
dpid_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
+dpidc_LDADD = ../dpip/libDpip.a ../dlib/libDlib.a
-EXTRA_DIST = dpidc
-bin_SCRIPTS = dpidc
+EXTRA_DIST = dpidrc.in
dpid_SOURCES = \
dpi.h \
- dpi_service.h \
dpi_socket_dir.h \
dpid.h \
dpid_common.h \
misc_new.h \
dpi.c \
- dpi_service.c \
dpi_socket_dir.c \
dpid.c \
dpid_common.c \
main.c \
misc_new.c
-install-data-local :
- $(mkinstalldirs) $(DESTDIR)$(sysconfdir)
- echo dpi_dir=$(libdir)/dillo/dpi > $(DESTDIR)$(sysconfdir)/dpidrc
- echo >> $(DESTDIR)$(sysconfdir)/dpidrc
- echo "proto.file=file/file.dpi" >> $(DESTDIR)$(sysconfdir)/dpidrc
- echo "proto.ftp=ftp/ftp.filter.dpi" >> $(DESTDIR)$(sysconfdir)/dpidrc
- echo "proto.https=https/https.filter.dpi" >> $(DESTDIR)$(sysconfdir)/dpidrc
- echo "proto.data=datauri/datauri.filter.dpi" >> $(DESTDIR)$(sysconfdir)/dpidrc
+dpidc_SOURCES = dpidc.c
+
+sysconf_DATA = dpidrc
+CLEANFILES = $(sysconf_DATA)
+
+dpidrc: $(srcdir)/dpidrc.in Makefile
+ sed -e 's|[@]libdir[@]|$(libdir)|' $(srcdir)/dpidrc.in > dpidrc
+
diff --git a/dpid/dpi.c b/dpid/dpi.c
index 45e5b49d..32bc628b 100644
--- a/dpid/dpi.c
+++ b/dpid/dpi.c
@@ -12,8 +12,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*! \file
@@ -23,6 +22,7 @@
*/
#include <errno.h>
+#include <stdlib.h> /* for exit */
#include "dpid_common.h"
#include "dpi.h"
#include "misc_new.h"
diff --git a/dpid/dpi.h b/dpid/dpi.h
index d97fdc6d..4ae33375 100644
--- a/dpid/dpi.h
+++ b/dpid/dpi.h
@@ -7,18 +7,8 @@
#ifndef DPI_H
#define DPI_H
-#include <config.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-/* Check the Unix98 goodie */
-#ifndef socklen_t
- #define socklen_t uint32_t
-#endif
+#include <unistd.h> /* for socklen_t */
+#include <sys/socket.h> /* for socklen_t and AF_LOCAL */
/* Some systems may not have this one... */
#ifndef AF_LOCAL
@@ -38,7 +28,8 @@
*/
enum {
UNKNOWN_CMD,
- BYE_CMD, /* "DpiBye" */
+ AUTH_CMD, /* authentication */
+ BYE_CMD, /* "DpiBye" */
CHECK_SERVER_CMD, /* "check_server" */
REGISTER_ALL_CMD, /* "register_all" */
REGISTER_SERVICE_CMD /* "register_service" */
diff --git a/dpid/dpi_service.c b/dpid/dpi_service.c
deleted file mode 100644
index 07cdad8e..00000000
--- a/dpid/dpi_service.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/*! \file
- * \todo
- * This module should be removed because its original functions
- * have been removed or modified.
- * Put these functions in dpid.c
- */
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include "dpid_common.h"
-#include "dpid.h"
-#include "../dpip/dpip.h"
-
-#ifdef TEST
-#include "testdat.h"
-#endif
-
-/* exported functions */
-char *get_dpi_dir(char *dpidrc);
-
-
-/*! Get dpi directory path from dpidrc
- * \Return
- * dpi directory on success, NULL on failure
- * \Important
- * The dpi_dir definition in dpidrc must have no leading white space.
- */
-char *get_dpi_dir(char *dpidrc)
-{
- FILE *In;
- int len;
- char *rcline = NULL, *value = NULL, *p;
-
- if ((In = fopen(dpidrc, "r")) == NULL) {
- ERRMSG("dpi_dir", "fopen", errno);
- MSG_ERR(" - %s\n", dpidrc);
- return (NULL);
- }
-
- while ((rcline = dGetline(In)) != NULL) {
- if (strncmp(rcline, "dpi_dir", 7) == 0)
- break;
- dFree(rcline);
- }
- fclose(In);
-
- if (!rcline) {
- ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
- MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
- MSG_ERR("eg. dpi_dir=/usr/local/lib/dillo/dpi ");
- MSG_ERR("with no leading spaces.\n");
- value = NULL;
- } else {
- len = (int) strlen(rcline);
- if (len && rcline[len - 1] == '\n')
- rcline[len - 1] = 0;
-
- if ((p = strchr(rcline, '='))) {
- while (*++p == ' ');
- value = dStrdup(p);
- } else {
- ERRMSG("dpi_dir", "strchr", 0);
- MSG_ERR(" - '=' not found in %s\n", rcline);
- value = NULL;
- }
- }
-
- dFree(rcline);
- return (value);
-}
-
-/*! Send the list of available dpi IDs to a client
- * \Return
- * 1 on success, -1 on failure.
- *
-static int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num)
-{
- int i;
- char *buf;
- ssize_t wlen = 0;
-
- for (i = 0; i < srv_num && wlen != -1; i++) {
- d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
- "send_data", dpi_attr_list[i].id);
- wlen = write(sock, d_cmd, strlen(d_cmd));
- dFree(d_cmd);
- }
- if (wlen == -1) {
- ERRMSG("send_service_list", "write", errno);
- return (-1);
- }
- return (1);
-}
- */
diff --git a/dpid/dpi_service.h b/dpid/dpi_service.h
deleted file mode 100644
index af7679b3..00000000
--- a/dpid/dpi_service.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*! \file
- * \todo
- * This module should be removed because its original functions
- * have been removed or modified.
- * Put these functions in dpid.c
- */
-
-#ifndef DPI_SERVICE_H
-#define DPI_SERVICE_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include "dpid.h"
-
-char *get_dpi_dir(char *dpidrc);
-
-int send_service_list(int sock, struct dp *dpi_attr_list, int srv_num);
-
-#endif
diff --git a/dpid/dpi_socket_dir.c b/dpid/dpi_socket_dir.c
index 333ccf6a..09a24e2f 100644
--- a/dpid/dpi_socket_dir.c
+++ b/dpid/dpi_socket_dir.c
@@ -12,8 +12,7 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*! \file
diff --git a/dpid/dpid.c b/dpid/dpid.c
index 97e6414d..d8bfeb96 100644
--- a/dpid/dpid.c
+++ b/dpid/dpid.c
@@ -12,22 +12,26 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Main functions to set-up dpi information and to initialise sockets
*/
#include <errno.h>
+#include <stdlib.h> /* for exit */
+#include <fcntl.h> /* for F_SETFD, F_GETFD, FD_CLOEXEC */
+
#include <sys/stat.h>
#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+
#include <unistd.h>
#include "dpid_common.h"
#include "dpid.h"
#include "dpi.h"
#include "dpi_socket_dir.h"
-#include "dpi_service.h"
#include "misc_new.h"
#include "../dpip/dpip.h"
@@ -35,71 +39,18 @@
#define QUEUE 5
volatile sig_atomic_t caught_sigchld = 0;
+char *SharedKey = NULL;
-/*! Return the basename of a filename
- */
-static char *get_basename(char *filename)
-{
- char *p;
-
- if (filename && (p = strrchr(filename, '/'))) {
- filename = p + 1;
- }
- return filename;
-}
-
-/*! Close and remove the sockets in the
- * given dpi attribute list
+/*! Remove dpid_comm_keys file.
+ * This avoids that dillo instances connect to a stale port after dpid
+ * has exited (e.g. after a reboot).
*/
-void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
+void cleanup()
{
- int i;
-
- for (i = 0; i < numdpis; i++) {
- a_Misc_close_fd(dpi_attr_list[i].socket);
- (void) unlink(dpi_attr_list[i].sockpath);
- }
-}
-
-/*! Close and remove inactive dpi sockets
- * \Return
- * Number of active dpis.
- */
-int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
-{
- int i, active = 0;
-
- for (i = 0; i < numdpis; i++) {
- if (dpi_attr_list[i].pid == 1) {
- a_Misc_close_fd(dpi_attr_list[i].socket);
- (void) unlink(dpi_attr_list[i].sockpath);
- } else
- active++;
- }
- return (active);
-}
-
-/*! Remove sockets
- */
-void cleanup(char *socket_dir)
-{
- DIR *dir;
- struct dirent *dir_entry = NULL;
- char *sockpath;
-
- dir = opendir(socket_dir);
- if (dir == NULL) {
- ERRMSG("cleanup", "opendir", errno);
- return;
- }
- while ( (dir_entry = readdir(dir)) != NULL ) {
- if (dir_entry->d_name[0] == '.')
- continue;
- sockpath = dStrconcat(socket_dir, "/", dir_entry->d_name, NULL);
- unlink(sockpath);
- dFree(sockpath);
- }
- closedir(dir);
+ char *fname;
+ fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
+ unlink(fname);
+ dFree(fname);
}
/*! Free memory used to describe
@@ -115,10 +66,6 @@ void free_dpi_attr(struct dp *dpi_attr)
dFree(dpi_attr->path);
dpi_attr->path = NULL;
}
- if (dpi_attr->sockpath != NULL) {
- dFree(dpi_attr->sockpath);
- dpi_attr->sockpath = NULL;
- }
}
/*! Free memory used by the plugin list
@@ -152,53 +99,46 @@ void free_services_list(Dlist *s_list)
dList_free(s_list);
}
-/*! \todo
- * Remove terminator and est_terminator unless we really want to clean up
- * on abnormal exit.
- */
-#if 0
/*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
*/
-void terminator(int sig)
+static void terminator(int sig)
{
- (void) signal(SIGCHLD, SIG_DFL);
+ (void) sig; /* suppress unused parameter warning */
cleanup();
- (void) signal(sig, SIG_DFL);
- (void) raise(sig);
_exit(0);
}
/*! Establish handler for termination signals
* and register cleanup with atexit */
-void est_terminator(void)
+void est_dpi_terminator()
{
struct sigaction act;
sigset_t block;
- (void) sigemptyset(&block);
- (void) sigaddset(&block, SIGINT);
- (void) sigaddset(&block, SIGQUIT);
- (void) sigaddset(&block, SIGTERM);
- (void) sigaddset(&block, SIGSEGV);
+ sigemptyset(&block);
+ sigaddset(&block, SIGHUP);
+ sigaddset(&block, SIGINT);
+ sigaddset(&block, SIGQUIT);
+ sigaddset(&block, SIGTERM);
act.sa_handler = terminator;
act.sa_mask = block;
act.sa_flags = 0;
- if (sigaction(SIGINT, &act, NULL) ||
+ if (sigaction(SIGHUP, &act, NULL) ||
+ sigaction(SIGINT, &act, NULL) ||
sigaction(SIGQUIT, &act, NULL) ||
- sigaction(SIGTERM, &act, NULL) || sigaction(SIGSEGV, &act, NULL)) {
- ERRMSG("est_terminator", "sigaction", errno);
+ sigaction(SIGTERM, &act, NULL)) {
+ ERRMSG("est_dpi_terminator", "sigaction", errno);
exit(1);
}
if (atexit(cleanup) != 0) {
- ERRMSG("est_terminator", "atexit", 0);
+ ERRMSG("est_dpi_terminator", "atexit", 0);
MSG_ERR("Hey! atexit failed, how did that happen?\n");
exit(1);
}
}
-#endif
/*! Identify a given file
* Currently there is only one file type associated with dpis.
@@ -216,6 +156,56 @@ enum file_type get_file_type(char *file_name)
}
}
+/*! Get dpi directory path from dpidrc
+ * \Return
+ * dpi directory on success, NULL on failure
+ * \Important
+ * The dpi_dir definition in dpidrc must have no leading white space.
+ */
+char *get_dpi_dir(char *dpidrc)
+{
+ FILE *In;
+ int len;
+ char *rcline = NULL, *value = NULL, *p;
+
+ if ((In = fopen(dpidrc, "r")) == NULL) {
+ ERRMSG("dpi_dir", "fopen", errno);
+ MSG_ERR(" - %s\n", dpidrc);
+ return (NULL);
+ }
+
+ while ((rcline = dGetline(In)) != NULL) {
+ if (strncmp(rcline, "dpi_dir", 7) == 0)
+ break;
+ dFree(rcline);
+ }
+ fclose(In);
+
+ if (!rcline) {
+ ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
+ MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
+ MSG_ERR("e.g. dpi_dir=/usr/local/lib/dillo/dpi\n");
+ MSG_ERR("with no leading spaces.\n");
+ value = NULL;
+ } else {
+ len = (int) strlen(rcline);
+ if (len && rcline[len - 1] == '\n')
+ rcline[len - 1] = 0;
+
+ if ((p = strchr(rcline, '='))) {
+ while (*++p == ' ');
+ value = dStrdup(p);
+ } else {
+ ERRMSG("dpi_dir", "strchr", 0);
+ MSG_ERR(" - '=' not found in %s\n", rcline);
+ value = NULL;
+ }
+ }
+
+ dFree(rcline);
+ return (value);
+}
+
/*! Scans a service directory in dpi_dir and fills dpi_attr
* \Note
* Caller must allocate memory for dpi_attr.
@@ -231,7 +221,7 @@ int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
char *service_dir = NULL;
struct stat statinfo;
enum file_type ftype;
- int retval = -1;
+ int ret = -1;
DIR *dir_stream;
struct dirent *dir_entry = NULL;
@@ -255,13 +245,13 @@ int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
dpi_attr->path =
dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
dpi_attr->id = dStrdup(service);
- dpi_attr->sockpath = NULL;
+ dpi_attr->port = 0;
dpi_attr->pid = 1;
if (strstr(dpi_attr->path, ".filter") != NULL)
dpi_attr->filter = 1;
else
dpi_attr->filter = 0;
- retval = 0;
+ ret = 0;
break;
default:
break;
@@ -269,12 +259,12 @@ int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
}
closedir(dir_stream);
- if (retval != 0)
+ if (ret != 0)
MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
dpi_dir, service);
}
dFree(service_dir);
- return retval;
+ return ret;
}
/*! Register a service
@@ -289,7 +279,7 @@ int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
int register_service(struct dp *dpi_attr, char *service)
{
char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
- int retval = -1;
+ int ret = -1;
user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
user_service_dir =
@@ -313,12 +303,12 @@ int register_service(struct dp *dpi_attr, char *service)
/* Check home dir for dpis */
if (access(user_service_dir, F_OK) == 0) {
get_dpi_attr(user_dpi_dir, service, dpi_attr);
- retval = 0;
+ ret = 0;
} else { /* Check system wide dpis */
if ((dir = get_dpi_dir(dpidrc)) != NULL) {
if (access(dir, F_OK) == 0) {
get_dpi_attr(dir, service, dpi_attr);
- retval = 0;
+ ret = 0;
} else {
ERRMSG("register_service", "get_dpi_attr failed", 0);
}
@@ -330,7 +320,7 @@ int register_service(struct dp *dpi_attr, char *service)
dFree(user_service_dir);
dFree(dpidrc);
dFree(dir);
- return (retval);
+ return ret;
}
/*!
@@ -424,19 +414,18 @@ static int services_alpha_comp(const struct service *s1,
return -strcmp(s1->name, s2->name);
}
-/*! Add services reading a dpidrc file
- * each non empty or commented line has the form
- * service = path_relative_to_dpidir
- * \Return:
- * \li Returns number of available services on success
- * \li -1 on failure
- */
+/*! Add services reading a dpidrc file
+ * each non empty or commented line has the form
+ * service = path_relative_to_dpidir
+ * \Return:
+ * \li Returns number of available services on success
+ * \li -1 on failure
+ */
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
{
FILE *dpidrc_stream;
- char *p, *line = NULL;
- char *service, *path;
- int i;
+ char *p, *line = NULL, *service, *path;
+ int i, st;
struct service *s;
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
@@ -479,16 +468,18 @@ int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
*services_list = dList_new(8);
/* dpidrc parser loop */
- while ((line = dGetline(dpidrc_stream)) != NULL) {
-
- if (dParser_get_rc_pair(&line, &service, &path) == -1) {
- if (line[0] && line[0] != '#' && (!service || !path)) {
- MSG_ERR("Syntax error in %s: service=\"%s\" path=\"%s\"\n",
- dpidrc, service, path);
- }
+ for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
+ st = dParser_parse_rc_line(&line, &service, &path);
+ if (st < 0) {
+ MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
+ dpidrc, service, path);
+ continue;
+ } else if (st != 0) {
continue;
}
+ _MSG("dpid: service=%s, path=%s\n", service, path);
+
/* ignore dpi_dir silently */
if (strcmp(service, "dpi_dir") == 0)
continue;
@@ -507,9 +498,8 @@ int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
/* if the dpi exist bind service and dpi */
if (i < numdpis)
s->dp_index = i;
- dFree(line);
}
- fclose(dpidrc_stream);
+ fclose(dpidrc_stream);
dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
@@ -520,127 +510,142 @@ int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
return (dList_length(*services_list));
}
-/*! Initialise the service request socket
- * \Return:
- * \li Number of sockets (1 == success)
- * \li -1 on failure
+/*
+ * Return a socket file descriptor
+ * (useful to set socket options in a uniform way)
*/
-int init_srs_socket(char *sockdir)
+static int make_socket_fd()
{
- int retval = -1;
- struct sockaddr_un srs_sa;
- size_t sun_path_len;
- socklen_t addr_sz;
+ int ret, one = 1;
- srs_name = dStrconcat(sockdir, "/", SRS_NAME, NULL);
- FD_ZERO(&sock_set);
-
- /* Initialise srs, service request socket on startup */
- if ((srs = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
- ERRMSG("init_srs_socket", "socket", errno);
- return (retval); /* avoids nesting ifs too deeply */
+ if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+ ERRMSG("make_socket_fd", "socket", errno);
+ } else {
+ /* avoid delays when sending small pieces of data */
+ setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
}
- /* Set srs to close on exec */
- fcntl(srs, F_SETFD, FD_CLOEXEC | fcntl(srs, F_GETFD));
- srs_sa.sun_family = AF_LOCAL;
+ /* set some buffering to increase the transfer's speed */
+ //setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
+ // &sock_buflen, (socklen_t)sizeof(sock_buflen));
+
+ return ret;
+}
- sun_path_len = sizeof(srs_sa.sun_path);
- if (strlen(srs_name) > sun_path_len) {
- ERRMSG("init_srs_socket", "srs_name is too long", 0);
- MSG_ERR("\n - it should be <= %lu chars", (ulong_t)sun_path_len);
- MSG_ERR("\n - srs_name = %s\n", srs_name);
- return(retval);
+/*! Bind a socket port on localhost. Try to be close to base_port.
+ * \Return
+ * \li listening socket file descriptor on success
+ * \li -1 on failure
+ */
+int bind_socket_fd(int base_port, int *p_port)
+{
+ int sock_fd, port;
+ struct sockaddr_in sin;
+ int ok = 0, last_port = base_port + 50;
+
+ if ((sock_fd = make_socket_fd()) == -1) {
+ return (-1); /* avoids nested ifs */
}
- strncpy(srs_sa.sun_path, srs_name, sun_path_len);
- addr_sz = (socklen_t) D_SUN_LEN(&srs_sa);
+ /* Set the socket FD to close on exec */
+ fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
- if ((bind(srs, (struct sockaddr *) &srs_sa, addr_sz)) == -1) {
- if (errno == EADDRINUSE) {
- ERRMSG("init_srs_socket", "bind", errno);
- MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path);
- dpi_errno = dpid_srs_addrinuse;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ /* Try to bind a port on localhost */
+ for (port = base_port; port <= last_port; ++port) {
+ sin.sin_port = htons(port);
+ if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
+ if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
+ continue;
+ ERRMSG("bind_socket_fd", "bind", errno);
+ } else if (listen(sock_fd, QUEUE) == -1) {
+ ERRMSG("bind_socket_fd", "listen", errno);
} else {
- ERRMSG("init_srs_socket", "bind", errno);
- MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path);
+ *p_port = port;
+ ok = 1;
+ break;
}
- } else if (chmod(srs_sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
- ERRMSG("init_srs_socket", "chmod", errno);
- MSG_ERR("srs_sa.sun_path = %s\n", srs_sa.sun_path);
- } else if (listen(srs, QUEUE) == -1) {
- ERRMSG("init_srs_socket", "listen", errno);
+ }
+ if (port > last_port) {
+ MSG_ERR("Hey! Can't find an available port from %d to %d\n",
+ base_port, last_port);
+ }
+
+ return ok ? sock_fd : -1;
+}
+
+/*! Save the current port and a shared secret in a file so dillo can find it.
+ * \Return:
+ * \li -1 on failure
+ */
+int save_comm_keys(int srs_port)
+{
+ int fd, ret = -1;
+ char *fname, port_str[32];
+
+ fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
+ fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ dFree(fname);
+ if (fd == -1) {
+ MSG("save_comm_keys: open %s\n", dStrerror(errno));
} else {
- retval = 1;
+ snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
+ if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+/*! Initialise the service request socket (IDS)
+ * \Return:
+ * \li Number of sockets (1 == success)
+ * \li -1 on failure
+ */
+int init_ids_srs_socket()
+{
+ int srs_port, ret = -1;
+
+ FD_ZERO(&sock_set);
+
+ if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
+ /* create the shared secret */
+ SharedKey = a_Misc_mksecret(8);
+ /* save port number and SharedKey */
+ if (save_comm_keys(srs_port) != -1) {
+ FD_SET(srs_fd, &sock_set);
+ ret = 1;
+ }
}
- FD_SET(srs, &sock_set);
- return (retval);
+ return ret;
}
-/*! Initialise a single dpi socket
+/*! Initialize a single dpi socket
* \Return
* \li 1 on success
* \li -1 on failure
*/
-int init_dpi_socket(struct dp *dpi_attr, char *sockdir)
+int init_dpi_socket(struct dp *dpi_attr)
{
- int caught_error = 0, s;
- char *dpi_nm; /* pointer to basename in dpi_attr->path */
- struct sockaddr_un sa;
- size_t sp_len;
- socklen_t addr_sz;
- size_t sock_buflen = 8192;
-
- sp_len = sizeof(sa.sun_path);
- if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
- ERRMSG("init_all_dpi_sockets", "socket", errno);
- return (-1); /* avoids nested ifs */
- }
- /* Set the socket FD to close on exec */
- fcntl(s, F_SETFD, FD_CLOEXEC | fcntl(s, F_GETFD));
+ int s_fd, port, ret = -1;
- /* set some buffering to increase the transfer's speed */
- setsockopt(s, SOL_SOCKET, SO_SNDBUF,
- &sock_buflen, (socklen_t)sizeof(sock_buflen));
-
- dpi_attr->socket = s;
- dpi_attr->sa.sun_family = AF_LOCAL;
- dpi_nm = get_basename(dpi_attr->path);
-
- dpi_attr->sockpath = dStrconcat(sockdir, "/", dpi_nm, "-XXXXXX", NULL);
- a_Misc_mkfname(dpi_attr->sockpath);
- if (strlen(dpi_attr->sockpath) > sp_len) {
- ERRMSG("init_all_dpi_sockets", "socket path is too long", 0);
- MSG_ERR("\n - it should be <= %lu chars", (ulong_t)sp_len);
- MSG_ERR("\n - socket path = %s\n", dpi_attr->sockpath);
- return(-1);
- }
- strncpy(dpi_attr->sa.sun_path, dpi_attr->sockpath, sp_len);
- addr_sz = (socklen_t) D_SUN_LEN(&dpi_attr->sa);
-
- if ((bind(s, (struct sockaddr *) &dpi_attr->sa, addr_sz)) == -1) {
- ERRMSG("init_all_dpi_sockets", "bind", errno);
- MSG_ERR("%s\n", dpi_attr->sa.sun_path);
- caught_error = 1;
- } else if (chmod(dpi_attr->sa.sun_path, S_IRUSR | S_IWUSR) == -1) {
- ERRMSG("init_all_dpi_sockets", "chmod", errno);
- MSG_ERR("%s\n", dpi_attr->sa.sun_path);
- caught_error = 1;
- } else if (listen(s, QUEUE) == -1) {
- ERRMSG("init_all_dpi_sockets", "listen", errno);
- caught_error = 1;
- }
-
- if (caught_error) {
- return (-1);
- } else {
- FD_SET(s, &sock_set);
- return (1);
+ if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
+ dpi_attr->sock_fd = s_fd;
+ dpi_attr->port = port;
+ FD_SET(s_fd, &sock_set);
+ ret = 1;
}
+
+ return ret;
}
/*! Setup sockets for the plugins and add them to
- * to the set of sockets (sock_set) watched by select.
+ * the set of sockets (sock_set) watched by select.
* \Return
* \li Number of sockets on success
* \li -1 on failure
@@ -649,17 +654,13 @@ int init_dpi_socket(struct dp *dpi_attr, char *sockdir)
* \Uses
* numdpis, srs, srs_name
*/
-int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir)
+int init_all_dpi_sockets(struct dp *dpi_attr_list)
{
int i;
- struct sockaddr_un sa;
- size_t sp_len;
-
- sp_len = sizeof(sa.sun_path);
/* Initialise sockets for each dpi */
for (i = 0; i < numdpis; i++) {
- if (init_dpi_socket(dpi_attr_list + i, sockdir) == -1)
+ if (init_dpi_socket(dpi_attr_list + i) == -1)
return (-1);
numsocks++;
}
@@ -688,7 +689,7 @@ void handle_sigchld(void)
for (i = 0; i < numdpis; i++) {
if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
dpi_attr_list[i].pid = 1;
- FD_SET(dpi_attr_list[i].socket, &sock_set);
+ FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
numsocks++;
}
}
@@ -714,45 +715,62 @@ void est_dpi_sigchld(void)
}
}
+/*! EINTR aware connect() call */
+int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
+{
+ ssize_t ret;
+
+ do {
+ ret = connect(sock_fd, addr, len);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ ERRMSG("dpid.c", "connect", errno);
+ }
+ return ret;
+}
+
/*! Send DpiBye command to all active non-filter dpis
*/
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
{
- static char *DpiBye_cmd = NULL;
- int i, dpi_socket;
- struct sockaddr_un dpi_addr;
- struct sockaddr_un sa;
- size_t sun_path_len, addr_len;
+ char *bye_cmd, *auth_cmd;
+ int i, sock_fd;
+ struct sockaddr_in sin;
- if (!DpiBye_cmd)
- DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
+ bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
+ auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
- sun_path_len = sizeof(sa.sun_path);
- addr_len = sizeof(dpi_addr);
-
- dpi_addr.sun_family = AF_LOCAL;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
for (i = 0; i < numdpis; i++) {
/* Skip inactive dpis and filters */
if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
continue;
- if ((dpi_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
+ if ((sock_fd = make_socket_fd()) == -1) {
ERRMSG("stop_active_dpis", "socket", errno);
+ continue;
}
- if (strlen(dpi_attr_list[i].sockpath) > sun_path_len) {
- ERRMSG("stop_active_dpis", "socket path is too long", 0);
- MSG_ERR("\n - it should be <= %lu chars",(ulong_t)sun_path_len);
- MSG_ERR("\n - socket path = %s\n", dpi_attr_list[i].sockpath);
- }
- strncpy(dpi_addr.sun_path, dpi_attr_list[i].sockpath, sun_path_len);
- if (connect(dpi_socket, (struct sockaddr *) &dpi_addr, addr_len) == -1) {
+
+ sin.sin_port = htons(dpi_attr_list[i].port);
+ if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
ERRMSG("stop_active_dpis", "connect", errno);
- MSG_ERR("%s\n", dpi_addr.sun_path);
+ MSG_ERR("%s\n", dpi_attr_list[i].path);
+ } else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
+ ERRMSG("stop_active_dpis", "write", errno);
+ } else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
+ ERRMSG("stop_active_dpis", "write", errno);
}
- (void) write(dpi_socket, DpiBye_cmd, strlen(DpiBye_cmd));
- a_Misc_close_fd(dpi_socket);
+ a_Misc_close_fd(sock_fd);
}
+
+ dFree(auth_cmd);
+ dFree(bye_cmd);
+
+ /* Allow child dpis some time to read dpid_comm_keys before erasing it */
+ sleep (1);
}
/*! Removes dpis in dpi_attr_list from the
@@ -764,8 +782,8 @@ void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
int i;
for (i = 0; i < numdpis; i++) {
- FD_CLR(dpi_attr_list[i].socket, &sock_set);
- a_Misc_close_fd(dpi_attr_list[i].socket);
+ FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
+ a_Misc_close_fd(dpi_attr_list[i].sock_fd);
}
}
@@ -776,20 +794,19 @@ void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
* \Return
* Number of available dpis
*/
-int register_all_cmd(char *sockdir)
+int register_all_cmd()
{
stop_active_dpis(dpi_attr_list, numdpis);
- rm_dpi_sockets(dpi_attr_list, numdpis);
free_plugin_list(&dpi_attr_list, numdpis);
free_services_list(services_list);
services_list = NULL;
numdpis = 0;
numsocks = 1; /* the srs socket */
FD_ZERO(&sock_set);
- FD_SET(srs, &sock_set);
+ FD_SET(srs_fd, &sock_set);
numdpis = register_all(&dpi_attr_list);
fill_services_list(dpi_attr_list, numdpis, &services_list);
- numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
+ numsocks = init_all_dpi_sockets(dpi_attr_list);
return (numdpis);
}
@@ -798,16 +815,16 @@ int register_all_cmd(char *sockdir)
* \Return
* message on success, NULL on failure
*/
-char *get_message(int sock, char *dpi_tag)
+char *get_message(int sock_fd, char *dpi_tag)
{
char *msg, *d_cmd;
- msg = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "msg");
+ msg = a_Dpip_get_attr(dpi_tag, "msg");
if (msg == NULL) {
ERRMSG("get_message", "failed to parse msg", 0);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Failed to parse request");
- (void) CKD_WRITE(sock, d_cmd);
+ (void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
}
return (msg);
@@ -832,40 +849,30 @@ int service_match(const struct service *A, const char *B)
}
/*!
- * Send socket path that matches dpi_id to client
+ * Send socket port that matches dpi_id to client
*/
-void send_sockpath(int sock, char *dpi_tag, struct dp *dpi_attr_list)
+void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
{
int i;
- char *dpi_id;
- char *d_cmd;
+ char *dpi_id, *d_cmd, port_str[16];
struct service *serv;
- dReturn_if_fail((dpi_id = get_message(sock, dpi_tag)) != NULL);
+ dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
if (serv == NULL || (i = serv->dp_index) == -1)
for (i = 0; i < numdpis; i++)
if (!strncmp(dpi_attr_list[i].id, dpi_id,
- dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
+ dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
break;
if (i < numdpis) {
/* found */
- if (access(dpi_attr_list[i].path, F_OK) == -1) {
- ERRMSG("send_sockpath", "access", errno);
- MSG_ERR(" - %s\n", dpi_attr_list[i].sockpath);
- d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
- "DpiError", "Plugin currently unavailable");
- (void) CKD_WRITE(sock, d_cmd);
- dFree(d_cmd);
- } else {
- d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
- "send_data", dpi_attr_list[i].sockpath);
- (void) CKD_WRITE(sock, d_cmd);
- dFree(d_cmd);
- }
+ snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
+ d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
+ (void) CKD_WRITE(sock_fd, d_cmd);
+ dFree(d_cmd);
}
dFree(dpi_id);
diff --git a/dpid/dpid.h b/dpid/dpid.h
index 74686087..8ef67dd5 100644
--- a/dpid/dpid.h
+++ b/dpid/dpid.h
@@ -5,42 +5,38 @@
#ifndef DPID_H
#define DPID_H
-#include <assert.h>
-#include <signal.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <string.h>
#include <sys/socket.h>
-#include <sys/time.h>
+#include <sys/select.h> /* for fd_set */
#include <sys/un.h>
-#include <errno.h>
+#include <signal.h> /* for sig_atomic_t */
+#include <netinet/in.h> /* for ntohl, IPPORT_USERRESERVED and stuff */
#include "d_size.h"
+/* FreeBSD 6.4 doesn't have it */
+#ifndef IPPORT_USERRESERVED
+ #define IPPORT_USERRESERVED 5000
+#endif
#define PATH_LEN 50
#define CMDLEN 20
#define MSGLEN 50
+#define DPID_BASE_PORT (IPPORT_USERRESERVED + 20)
+
/*! \TODO: Should read this from dillorc */
#define SRS_NAME "dpid.srs"
char *srs_name;
-/*! dpid service request socket */
-int srs;
+/*! dpid's service request socket file descriptor */
+int srs_fd;
/*! plugin state information
*/
struct dp {
char *id;
char *path;
- char *sockpath;
- int socket;
- struct sockaddr_un sa;
+ int sock_fd;
+ int port;
pid_t pid;
int filter;
};
@@ -72,9 +68,7 @@ extern volatile sig_atomic_t caught_sigchld;
void rm_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
-int rm_inactive_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
-
-void cleanup(char *socket_dir);
+void cleanup();
void free_dpi_attr(struct dp *dpi_attr);
@@ -92,11 +86,11 @@ int register_all(struct dp **attlist);
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list);
-int init_srs_socket(char *sockdir);
+int init_ids_srs_socket();
-int init_dpi_socket(struct dp *dpi_attr, char *sockdir);
+int init_dpi_socket(struct dp *dpi_attr);
-int init_all_dpi_sockets(struct dp *dpi_attr_list, char *sockdir);
+int init_all_dpi_sockets(struct dp *dpi_attr_list);
void dpi_sigchld(int sig);
@@ -104,16 +98,18 @@ void handle_sigchld(void);
void est_dpi_sigchld(void);
+void est_dpi_terminator(void);
+
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis);
void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis);
-int register_all_cmd(char *sockdir);
+int register_all_cmd();
char *get_message(int sock, char *dpi_tag);
int service_match(const struct service *A, const char *B);
-void send_sockpath(int sock, char * dpi_tag, struct dp *dpi_attr_list);
+void send_sockport(int sock_fd, char * dpi_tag, struct dp *dpi_attr_list);
#endif
diff --git a/dpid/dpid_common.c b/dpid/dpid_common.c
index a04d9c4f..a903db95 100644
--- a/dpid/dpid_common.c
+++ b/dpid/dpid_common.c
@@ -1,5 +1,16 @@
+/*
+ * File: dpid_common.c
+ *
+ * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <errno.h>
#include <stdio.h>
-#include <string.h>
#include <unistd.h>
#include "dpid_common.h"
@@ -41,3 +52,22 @@ ssize_t ckd_write(int fd, char *msg, char *file, int line)
}
return (ret);
}
+
+/*!
+ * Provides an error checked close() call.
+ * Call this via the CKD_CLOSE macro
+ * \return close return value
+ */
+ssize_t ckd_close(int fd, char *file, int line)
+{
+ ssize_t ret;
+
+ do {
+ ret = close(fd);
+ } while (ret == -1 && errno == EINTR);
+ if (ret == -1) {
+ MSG_ERR("%s:%d: close: %s\n", file, line, dStrerror(errno));
+ }
+ return (ret);
+}
+
diff --git a/dpid/dpid_common.h b/dpid/dpid_common.h
index 4311a8a8..ed886e19 100644
--- a/dpid/dpid_common.h
+++ b/dpid/dpid_common.h
@@ -9,9 +9,6 @@
* the next patch
*/
-#include <stdio.h>
-#include <errno.h>
-#include <sys/types.h>
#include <dirent.h>
#include "../dlib/dlib.h"
@@ -26,15 +23,18 @@
#define dotDILLO_DPI ".dillo/dpi"
#define dotDILLO_DPIDRC ".dillo/dpidrc"
+#define dotDILLO_DPID_COMM_KEYS ".dillo/dpid_comm_keys"
+
#define ERRMSG(CALLER, CALLED, ERR)\
errmsg(CALLER, CALLED, ERR, __FILE__, __LINE__)
#define _ERRMSG(CALLER, CALLED, ERR)
/*!
- * Macro for calling the ckd_write function
+ * Macros for calling ckd_write and ckd_close functions
*/
#define CKD_WRITE(fd, msg) ckd_write(fd, msg, __FILE__, __LINE__)
+#define CKD_CLOSE(fd) ckd_close(fd, __FILE__, __LINE__)
/*! Error codes for dpid */
@@ -57,5 +57,6 @@ void errmsg(char *caller, char *called, int errornum, char *file, int line);
int no_dotfiles(const struct dirent *filedat);
ssize_t ckd_write(int fd, char *msg, char *file, int line);
+ssize_t ckd_close(int fd, char *file, int line);
#endif
diff --git a/dpid/dpidc b/dpid/dpidc
deleted file mode 100644
index 88b887cb..00000000
--- a/dpid/dpidc
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/perl -w
-# Author: Ferdi Franceschini
-#
-# dpid control program
-# Currently allows
-# register: Tells dpid to register all available dpis
-# stop: Stops dpid.
-
-use strict;
-use IO::Socket::UNIX;
-
-# Get socket directory name
-open(DSD, "<$ENV{HOME}/.dillo/dpi_socket_dir");
-my $dir = <DSD>;
-close(DSD);
-
-my $socket = IO::Socket::UNIX->new(Peer => "$dir/dpid.srs", Type => SOCK_STREAM, Timeout => 1000 ) or die "new: $@";
-
-$socket->autoflush(1);
-
-my %dpi_command = (
- "register" => "<dpi cmd='register_all' '>",
- "stop" => "<dpi cmd='DpiBye' '>",
- );
-
-if ( exists($dpi_command{$ARGV[0]}) ) {
- print $socket $dpi_command{$ARGV[0]};
-} else {
- close($socket);
- print "Usage: dpidc register|stop\n";
-}
diff --git a/dpid/dpidc.c b/dpid/dpidc.c
new file mode 100644
index 00000000..61a91275
--- /dev/null
+++ b/dpid/dpidc.c
@@ -0,0 +1,121 @@
+#include <stdio.h>
+#include <stdlib.h> /* for exit */
+#include <string.h> /* for bzero */
+#include <unistd.h> /* for read and write */
+#include <ctype.h> /* for isxdigit */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+
+#include "../dpip/dpip.h"
+
+#define MSG_ERR(...) printf("** ERROR **: " __VA_ARGS__);
+
+char *CMD_REGISTER = "<cmd='register_all' '>";
+char *CMD_STOP = "<cmd='DpiBye' '>";
+
+static char SharedKey[32];
+
+static void print_usage(const char *prgname)
+{
+ fprintf(stderr,"Control program for the Dillo plugin daemon\n"
+ "Usage: %s {stop|register|chat}\n\n", prgname);
+}
+
+static void error(char *msg)
+{
+ perror(msg);
+ exit(1);
+}
+
+/*
+ * Read dpid's communication keys from its saved file.
+ * Return value: 1 on success, -1 on error.
+ */
+static int Dpi_read_comm_keys(int *port)
+{
+ FILE *In;
+ char *fname, *rcline = NULL, *tail;
+ int i, ret = -1;
+
+ fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
+ if ((In = fopen(fname, "r")) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
+ } else if ((rcline = dGetline(In)) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
+ } else {
+ *port = strtol(rcline, &tail, 10);
+ for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
+ SharedKey[i] = tail[i+1];
+ SharedKey[i] = 0;
+ ret = 1;
+ }
+ dFree(rcline);
+ dFree(fname);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int sockfd, portno, n;
+ struct sockaddr_in serv_addr;
+ char buffer[256];
+
+ if (argc != 2) {
+ print_usage(argv[0]);
+ exit(1);
+ }
+
+ /* Read dpid's port number from saved file */
+ if (Dpi_read_comm_keys(&portno) == -1) {
+ MSG_ERR("main: Can't read dpid's port number\n");
+ exit(1);
+ }
+
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sockfd < 0)
+ error("ERROR opening socket");
+ bzero((char *) &serv_addr, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ serv_addr.sin_port = htons(portno);
+ if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
+ error("ERROR connecting");
+
+ snprintf(buffer, sizeof(buffer), "<cmd='auth' msg='%s' '>", SharedKey);
+ n = write(sockfd, buffer, strlen(buffer));
+ if (n < 0)
+ error("ERROR writing to socket");
+
+ if (strcmp(argv[1], "stop") == 0) {
+ strcpy(buffer, CMD_STOP);
+ } else if (strcmp(argv[1], "register") == 0) {
+ strcpy(buffer, CMD_REGISTER);
+ } else if (strcmp(argv[1], "chat") == 0) {
+ printf("Please enter the message: ");
+ bzero(buffer,256);
+ if (fgets(buffer,255,stdin) == NULL)
+ MSG_ERR("dpidc: Can't read the message\n");
+ } else {
+ MSG_ERR("main: Unknown operation '%s'\n", argv[1]);
+ print_usage(argv[0]);
+ exit(1);
+ }
+
+ n = write(sockfd,buffer,strlen(buffer));
+ if (n < 0)
+ error("ERROR writing to socket");
+/*
+ bzero(buffer,256);
+ n = read(sockfd,buffer,255);
+ if (n < 0)
+ error("ERROR reading from socket");
+ printf("%s\n",buffer);
+*/
+ close(sockfd);
+ return 0;
+}
diff --git a/dpid/dpidrc.in b/dpid/dpidrc.in
new file mode 100644
index 00000000..23374d96
--- /dev/null
+++ b/dpid/dpidrc.in
@@ -0,0 +1,6 @@
+dpi_dir=@libdir@/dillo/dpi
+
+proto.file=file/file.dpi
+proto.ftp=ftp/ftp.filter.dpi
+proto.https=https/https.filter.dpi
+proto.data=datauri/datauri.filter.dpi
diff --git a/dpid/main.c b/dpid/main.c
index b47ca627..5f512245 100644
--- a/dpid/main.c
+++ b/dpid/main.c
@@ -12,16 +12,15 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <errno.h>
-#include <unistd.h>
-#include <limits.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <assert.h>
+#include <errno.h> /* for ckd_write */
+#include <unistd.h> /* for ckd_write */
+#include <stdlib.h> /* for exit */
+#include <assert.h> /* for assert */
+#include <sys/stat.h> /* for umask */
+
#include "dpid_common.h"
#include "dpid.h"
#include "dpi.h"
@@ -46,7 +45,7 @@ static int start_filter_plugin(struct dp dpi_attr)
csz = (socklen_t) sizeof(clnt_addr);
- newsock = accept(dpi_attr.socket, (struct sockaddr *) &clnt_addr, &csz);
+ newsock = accept(dpi_attr.sock_fd, (struct sockaddr *) &clnt_addr, &csz);
if (newsock == -1)
ERRMSG("start_plugin", "accept", errno);
@@ -69,7 +68,7 @@ static int start_filter_plugin(struct dp dpi_attr)
}
if (pid == 0) {
/* Child, start plugin */
- if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
+ if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
ERRMSG("start_plugin", "execl", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
@@ -91,17 +90,17 @@ static int start_filter_plugin(struct dp dpi_attr)
static void start_server_plugin(struct dp dpi_attr)
{
- if (dup2(dpi_attr.socket, STDIN_FILENO) == -1) {
+ if (dup2(dpi_attr.sock_fd, STDIN_FILENO) == -1) {
ERRMSG("start_plugin", "dup2", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
- if (a_Misc_close_fd(dpi_attr.socket) == -1) {
+ if (a_Misc_close_fd(dpi_attr.sock_fd) == -1) {
ERRMSG("start_plugin", "close", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
}
- if (execl(dpi_attr.path, dpi_attr.path, NULL) == -1) {
+ if (execl(dpi_attr.path, dpi_attr.path, (char*)NULL) == -1) {
ERRMSG("start_plugin", "execl", errno);
MSG_ERR("ERROR in child proc for %s\n", dpi_attr.path);
exit(1);
@@ -113,32 +112,15 @@ static void start_server_plugin(struct dp dpi_attr)
* \Return
* pointer to dynamically allocated request tag
*/
-static char *get_request(int sock)
+static char *get_request(Dsh *sh)
{
- char *req, buf[10];
- size_t buflen;
- size_t rqsz;
- ssize_t rdln;
-
- req = NULL;
- buf[0] = '\0';
- buflen = sizeof(buf) / sizeof(buf[0]);
+ char *dpip_tag;
(void) sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
- for (rqsz = 0; (rdln = read(sock, buf, buflen)) != 0; rqsz += rdln) {
- if (rdln == -1)
- break;
- req = (char *) realloc(req, rqsz + rdln + 1);
- if (rqsz == 0)
- req[0] = '\0';
- strncat(req, buf, (size_t) rdln);
- }
+ dpip_tag = a_Dpip_dsh_read_token(sh, 1);
(void) sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
- if (rdln == -1) {
- ERRMSG("get_request", "read", errno);
- }
- return (req);
+ return dpip_tag;
}
/*!
@@ -146,7 +128,7 @@ static char *get_request(int sock)
* \Return
* command code on success, -1 on failure
*/
-static int get_command(int sock, char *dpi_tag)
+static int get_command(Dsh *sh, char *dpi_tag)
{
char *cmd, *d_cmd;
int COMMAND;
@@ -156,16 +138,18 @@ static int get_command(int sock, char *dpi_tag)
return (-1);
}
- cmd = a_Dpip_get_attr(dpi_tag, strlen(dpi_tag), "cmd");
+ cmd = a_Dpip_get_attr(dpi_tag, "cmd");
if (cmd == NULL) {
ERRMSG("get_command", "a_Dpip_get_attr", 0);
MSG_ERR(": dpid failed to parse cmd in %s\n", dpi_tag);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Failed to parse request");
- (void) CKD_WRITE(sock, d_cmd);
+ a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
COMMAND = -1;
+ } else if (strcmp("auth", cmd) == 0) {
+ COMMAND = AUTH_CMD;
} else if (strcmp("DpiBye", cmd) == 0) {
COMMAND = BYE_CMD;
} else if (strcmp("check_server", cmd) == 0) {
@@ -221,7 +205,6 @@ static int get_open_max(void)
int main(void)
{
int i, n = 0, open_max;
- char *dirname = NULL, *sockdir = NULL;
int dpid_idle_timeout = 60 * 60; /* default, in seconds */
struct timeval select_timeout;
sigset_t mask_none;
@@ -251,6 +234,7 @@ int main(void)
/* Get list of available dpis */
numdpis = register_all(&dpi_attr_list);
+#if 0
/* Get name of socket directory */
dirname = a_Dpi_sockdir_file();
if ((sockdir = init_sockdir(dirname)) == NULL) {
@@ -258,14 +242,16 @@ int main(void)
MSG_ERR("Failed to create socket directory\n");
exit(1);
}
+#endif
/* Init and get services list */
fill_services_list(dpi_attr_list, numdpis, &services_list);
/* Remove any sockets that may have been leftover from a crash */
- cleanup(sockdir);
+ //cleanup();
+
/* Initialise sockets */
- if ((numsocks = init_srs_socket(sockdir)) == -1) {
+ if ((numsocks = init_ids_srs_socket()) == -1) {
switch (dpi_errno) {
case dpid_srs_addrinuse:
MSG_ERR("dpid refuses to start, possibly because:\n");
@@ -273,12 +259,13 @@ int main(void)
MSG_ERR("\t2) A previous dpid didn't clean up on exit.\n");
exit(1);
default:
- ERRMSG("main", "init_srs_sockets failed", 0);
+ //ERRMSG("main", "init_srs_socket failed", 0);
+ ERRMSG("main", "init_ids_srs_socket failed", 0);
exit(1);
}
}
- numsocks = init_all_dpi_sockets(dpi_attr_list, sockdir);
- //est_terminator(); /* Do we still want to clean up on an abnormal exit? */
+ numsocks = init_all_dpi_sockets(dpi_attr_list);
+ est_dpi_terminator();
est_dpi_sigchld();
(void) sigemptyset(&mask_sigchld);
@@ -309,7 +296,7 @@ int main(void)
continue;
stop_active_dpis(dpi_attr_list, numdpis);
- cleanup(sockdir);
+ //cleanup();
exit(0);
}
} while (n == -1 && errno == EINTR);
@@ -319,42 +306,51 @@ int main(void)
exit(1);
}
/* If the service req socket is selected then service the req. */
- if (FD_ISSET(srs, &selected_set)) {
- int sock;
- socklen_t csz;
- struct sockaddr_un clnt_addr;
+ if (FD_ISSET(srs_fd, &selected_set)) {
+ int sock_fd;
+ socklen_t sin_sz;
+ struct sockaddr_in sin;
char *req = NULL;
--n;
assert(n >= 0);
- csz = (socklen_t) sizeof(clnt_addr);
- sock = accept(srs, (struct sockaddr *) &clnt_addr, &csz);
- if (sock == -1) {
+ sin_sz = (socklen_t) sizeof(sin);
+ sock_fd = accept(srs_fd, (struct sockaddr *)&sin, &sin_sz);
+ if (sock_fd == -1) {
ERRMSG("main", "accept", errno);
MSG_ERR("accept on srs socket failed\n");
MSG_ERR("service pending connections, and continue\n");
} else {
int command;
+ Dsh *sh;
- req = get_request(sock);
- command = get_command(sock, req);
+ sh = a_Dpip_dsh_new(sock_fd, sock_fd, 1024);
+read_next:
+ req = get_request(sh);
+ command = get_command(sh, req);
switch (command) {
+ case AUTH_CMD:
+ if (a_Dpip_check_auth(req) != -1) {
+ dFree(req);
+ goto read_next;
+ }
+ break;
case BYE_CMD:
stop_active_dpis(dpi_attr_list, numdpis);
- cleanup(sockdir);
+ //cleanup();
exit(0);
break;
case CHECK_SERVER_CMD:
- send_sockpath(sock, req, dpi_attr_list);
+ send_sockport(sock_fd, req, dpi_attr_list);
break;
case REGISTER_ALL_CMD:
- register_all_cmd(sockdir);
+ register_all_cmd();
break;
case UNKNOWN_CMD:
{
char *d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Unknown command");
- (void) CKD_WRITE(sock, d_cmd);
+ (void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
ERRMSG("main", "Unknown command", 0);
MSG_ERR(" for request: %s\n", req);
@@ -366,14 +362,15 @@ int main(void)
}
if (req)
free(req);
- a_Misc_close_fd(sock);
+ a_Dpip_dsh_close(sh);
+ a_Dpip_dsh_free(sh);
}
}
/* While there's a request on one of the plugin sockets
* find the matching plugin and start it. */
for (i = 0; n > 0 && i < numdpis; i++) {
- if (FD_ISSET(dpi_attr_list[i].socket, &selected_set)) {
+ if (FD_ISSET(dpi_attr_list[i].sock_fd, &selected_set)) {
--n;
assert(n >= 0);
@@ -387,11 +384,12 @@ int main(void)
* on its socket */
numsocks--;
assert(numsocks >= 0);
- FD_CLR(dpi_attr_list[i].socket, &sock_set);
+ FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
if ((dpi_attr_list[i].pid = fork()) == -1) {
ERRMSG("main", "fork", errno);
/* exit(1); */
} else if (dpi_attr_list[i].pid == 0) {
+ /* child */
(void) sigprocmask(SIG_SETMASK, &mask_none, NULL);
start_server_plugin(dpi_attr_list[i]);
}
diff --git a/dpid/misc_new.c b/dpid/misc_new.c
index f85f43e4..7f963aed 100644
--- a/dpid/misc_new.c
+++ b/dpid/misc_new.c
@@ -1,17 +1,23 @@
-#include <stdio.h>
-#include <time.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * File: misc_new.c
+ *
+ * Copyright 2008 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <errno.h> /* errno, err-codes */
#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <errno.h>
-#include <fcntl.h>
-#include "d_size.h"
-#include "misc_new.h"
-#include "dpid_common.h"
+#include <time.h>
+#include <sys/stat.h> /* stat */
+#include <stdlib.h> /* rand, srand */
-#include "misc_new.h" /* for function prototypes */
+#include "../dlib/dlib.h"
+#include "dpid_common.h"
+#include "misc_new.h" /* for function prototypes */
/*
@@ -159,7 +165,7 @@ char *a_Misc_mkfname(char *template)
{
char *tmp = template + strlen(template) - 6;
int i;
- unsigned int random;
+ uint_t random;
struct stat stat_buf;
if (tmp < template)
@@ -185,3 +191,26 @@ char *a_Misc_mkfname(char *template)
MSG_ERR("a_Misc_mkfname: another round for %s \n", template);
}
}
+
+/*
+ * Return a new, random hexadecimal string of 'nchar' characters.
+ */
+char *a_Misc_mksecret(int nchar)
+{
+ int i;
+ uint_t random;
+ char *secret = dNew(char, nchar + 1);
+
+ srand((uint_t)(time(0) ^ getpid()));
+ random = (unsigned) rand();
+ for (i = 0; i < nchar; ++i) {
+ int hexdigit = (random >> (i * 5)) & 0x0f;
+
+ secret[i] = hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
+ }
+ secret[i] = 0;
+ MSG("a_Misc_mksecret: %s\n", secret);
+
+ return secret;
+}
+
diff --git a/dpid/misc_new.h b/dpid/misc_new.h
index e2bf6e9a..325451a1 100644
--- a/dpid/misc_new.h
+++ b/dpid/misc_new.h
@@ -1,13 +1,12 @@
#ifndef MISC_NEW_H
#define MISC_NEW_H
-#include "../dlib/dlib.h"
-
int a_Misc_close_fd(int fd);
Dstr *a_Misc_rdtag(int socket);
char *a_Misc_readtag(int sock);
char *a_Misc_mkdtemp(char *template);
char *a_Misc_mkfname(char *template);
+char *a_Misc_mksecret(int nchar);
#endif
diff --git a/dpip/dpip.c b/dpip/dpip.c
index c6212db9..46ce6ec8 100644
--- a/dpip/dpip.c
+++ b/dpip/dpip.c
@@ -10,15 +10,30 @@
*
*/
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
+#include <ctype.h>
+#include <unistd.h> /* for close */
+#include <fcntl.h> /* for fcntl */
-#include "../dlib/dlib.h"
#include "dpip.h"
#include "d_size.h"
-static char Quote = '\'';
+#define RBUF_SZ 16*1024
+//#define RBUF_SZ 1
+
+#define DPIP_TAG_END " '>"
+#define DPIP_MODE_SWITCH_TAG "cmd='start_send_page' "
+#define MSG_ERR(...) fprintf(stderr, "[dpip]: " __VA_ARGS__)
+
+/*
+ * Local variables
+ */
+static const char Quote = '\'';
+static const int DpipTag = 1;
/*
* Basically the syntax of a dpip tag is:
@@ -107,17 +122,19 @@ char *a_Dpip_build_cmd(const char *format, ...)
}
/*
- * Task: given a tag and an attribute name, return its value.
- * (stuffing of ' is removed here)
+ * Task: given a tag, its size and an attribute name, return the
+ * attribute value (stuffing of ' is removed here).
+ *
* Return value: the attribute value, or NULL if not present or malformed.
*/
-char *a_Dpip_get_attr(char *tag, size_t tagsize, const char *attrname)
+char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname)
{
uint_t i, n = 0, found = 0;
- char *p, *q, *start, *val = NULL;
+ const char *p, *q, *start;
+ char *r, *s, *val = NULL;
DpipTagParsingState state = SEEK_NAME;
- if (!attrname || !*attrname)
+ if (!tag || !tagsize || !attrname || !*attrname)
return NULL;
for (i = 1; i < tagsize && !found; ++i) {
@@ -156,13 +173,340 @@ char *a_Dpip_get_attr(char *tag, size_t tagsize, const char *attrname)
p = q + 2;
if (q && q[1] == ' ') {
val = dStrndup(start, (uint_t)(q - start));
- for (p = q = val; (*q = *p); ++p, ++q)
- if (*p == Quote && p[1] == p[0])
- ++p;
+ for (r = s = val; (*r = *s); ++r, ++s)
+ if (s[0] == Quote && s[0] == s[1])
+ ++s;
}
}
return val;
}
-/* ------------------------------------------------------------------------- */
+/*
+ * Task: given a tag and an attribute name, return its value.
+ * Return value: the attribute value, or NULL if not present or malformed.
+ */
+char *a_Dpip_get_attr(const char *tag, const char *attrname)
+{
+ return (tag ? a_Dpip_get_attr_l(tag, strlen(tag), attrname) : NULL);
+}
+
+/*
+ * Check whether the given 'auth' string equals what dpid saved.
+ * Return value: 1 if equal, -1 otherwise
+ */
+int a_Dpip_check_auth(const char *auth_tag)
+{
+ char SharedSecret[32];
+ FILE *In;
+ char *fname, *rcline = NULL, *tail, *cmd, *msg;
+ int i, port, ret = -1;
+
+ /* sanity checks */
+ if (!auth_tag ||
+ !(cmd = a_Dpip_get_attr(auth_tag, "cmd")) || strcmp(cmd, "auth") ||
+ !(msg = a_Dpip_get_attr(auth_tag, "msg"))) {
+ return ret;
+ }
+
+ fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
+ if ((In = fopen(fname, "r")) == NULL) {
+ MSG_ERR("[a_Dpip_check_auth] %s\n", dStrerror(errno));
+ } else if ((rcline = dGetline(In)) == NULL) {
+ MSG_ERR("[a_Dpip_check_auth] empty file: %s\n", fname);
+ } else {
+ port = strtol(rcline, &tail, 10);
+ for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
+ SharedSecret[i] = tail[i+1];
+ SharedSecret[i] = 0;
+ if (strcmp(msg, SharedSecret) == 0)
+ ret = 1;
+ }
+ if (In)
+ fclose(In);
+ dFree(rcline);
+ dFree(fname);
+ dFree(msg);
+ dFree(cmd);
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------
+ * Dpip socket API ----------------------------------------------------------
+ */
+
+/*
+ * Create and initialize a dpip socket handler
+ */
+Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz)
+{
+ Dsh *dsh = dNew(Dsh, 1);
+
+ /* init descriptors and streams */
+ dsh->fd_in = fd_in;
+ dsh->fd_out = fd_out;
+
+ /* init buffer */
+ dsh->wrbuf = dStr_sized_new(8 *1024);
+ dsh->rdbuf = dStr_sized_new(8 *1024);
+ dsh->flush_sz = flush_sz;
+ dsh->mode = DPIP_TAG;
+ if (fcntl(dsh->fd_in, F_GETFL) & O_NONBLOCK)
+ dsh->mode |= DPIP_NONBLOCK;
+ dsh->status = 0;
+
+ return dsh;
+}
+
+/*
+ * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error
+ */
+static int Dpip_dsh_write(Dsh *dsh, int nb, const char *Data, int DataSize)
+{
+ int req_mode, old_flags = 0, st, ret = -3, sent = 0;
+
+ req_mode = (nb) ? DPIP_NONBLOCK : 0;
+ if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
+ /* change mode temporarily... */
+ old_flags = fcntl(dsh->fd_out, F_GETFL);
+ fcntl(dsh->fd_out, F_SETFL,
+ (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK);
+ }
+
+ while (1) {
+ st = write(dsh->fd_out, Data + sent, DataSize - sent);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EAGAIN) {
+ dsh->status = DPIP_EAGAIN;
+ ret = -1;
+ break;
+ } else {
+ MSG_ERR("[Dpip_dsh_write] %s\n", dStrerror(errno));
+ dsh->status = DPIP_ERROR;
+ break;
+ }
+ } else {
+ sent += st;
+ if (nb || sent == DataSize) {
+ ret = sent;
+ break;
+ }
+ }
+ }
+
+ if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
+ /* restore old mode */
+ fcntl(dsh->fd_out, F_SETFL, old_flags);
+ }
+
+ return ret;
+}
+
+/*
+ * Streamed write to socket
+ * Return: 0 on success, 1 on error.
+ */
+int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize)
+{
+ int ret = 1;
+
+ /* append to buf */
+ dStr_append_l(dsh->wrbuf, Data, DataSize);
+
+ if (!flush || dsh->wrbuf->len == 0)
+ return 0;
+
+ ret = Dpip_dsh_write(dsh, 0, dsh->wrbuf->str, dsh->wrbuf->len);
+ if (ret == dsh->wrbuf->len) {
+ dStr_truncate(dsh->wrbuf, 0);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/*
+ * Return value: 0 on success or empty buffer,
+ * 1..DataSize sent, -1 eagain, or -3 on big Error
+ */
+int a_Dpip_dsh_tryflush(Dsh *dsh)
+{
+ int st;
+
+ if (dsh->wrbuf->len == 0) {
+ st = 0;
+ } else {
+ st = Dpip_dsh_write(dsh, 1, dsh->wrbuf->str, dsh->wrbuf->len);
+ if (st > 0) {
+ /* update internal buffer */
+ dStr_erase(dsh->wrbuf, 0, st);
+ }
+ }
+ return (dsh->wrbuf->len == 0) ? 0 : st;
+}
+
+/*
+ * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error
+ */
+int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize)
+{
+ int st;
+
+ if ((st = Dpip_dsh_write(dsh, 1, Data, DataSize)) > 0) {
+ /* update internal buffer */
+ if (st < DataSize)
+ dStr_append_l(dsh->wrbuf, Data + st, DataSize - st);
+ }
+ return st;
+}
+
+/*
+ * Convenience function.
+ */
+int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str)
+{
+ return a_Dpip_dsh_write(dsh, flush, str, (int)strlen(str));
+}
+
+/*
+ * Read raw data from the socket into our buffer in
+ * either BLOCKING or NONBLOCKING mode.
+ */
+static void Dpip_dsh_read(Dsh *dsh, int blocking)
+{
+ char buf[RBUF_SZ];
+ int req_mode, old_flags = 0, st, ret = -3, nb = !blocking;
+
+ dReturn_if (dsh->status == DPIP_ERROR || dsh->status == DPIP_EOF);
+
+ req_mode = (nb) ? DPIP_NONBLOCK : 0;
+ if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
+ /* change mode temporarily... */
+ old_flags = fcntl(dsh->fd_in, F_GETFL);
+ fcntl(dsh->fd_in, F_SETFL,
+ (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK);
+ }
+
+ while (1) {
+ st = read(dsh->fd_in, buf, RBUF_SZ);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EAGAIN) {
+ dsh->status = DPIP_EAGAIN;
+ ret = -1;
+ break;
+ } else {
+ MSG_ERR("[Dpip_dsh_read] %s\n", dStrerror(errno));
+ dsh->status = DPIP_ERROR;
+ break;
+ }
+ } else if (st == 0) {
+ dsh->status = DPIP_EOF;
+ break;
+ } else {
+ /* append to buf */
+ dStr_append_l(dsh->rdbuf, buf, st);
+ if (blocking)
+ break;
+ }
+ }
+
+ if ((dsh->mode & DPIP_NONBLOCK) != req_mode) {
+ /* restore old mode */
+ fcntl(dsh->fd_out, F_SETFL, old_flags);
+ }
+
+ /* assert there's no more data in the wire...
+ * (st < buf upon interrupt || st == buf and no more data) */
+ if (blocking)
+ Dpip_dsh_read(dsh, 0);
+}
+
+/*
+ * Return a newlly allocated string with the next dpip token in the socket.
+ * Return value: token string on success, NULL otherwise
+ */
+char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking)
+{
+ char *p, *ret = NULL;
+
+ /* Read all available data without blocking */
+ Dpip_dsh_read(dsh, 0);
+
+ /* switch mode upon request */
+ if (dsh->mode & DPIP_LAST_TAG)
+ dsh->mode = DPIP_RAW;
+
+ if (blocking) {
+ if (dsh->mode & DPIP_TAG) {
+ /* Only wait for data when the tag is incomplete */
+ if (!strstr(dsh->rdbuf->str, DPIP_TAG_END)) {
+ do {
+ Dpip_dsh_read(dsh, 1);
+ p = strstr(dsh->rdbuf->str, DPIP_TAG_END);
+ } while (!p && dsh->status == EAGAIN);
+ }
+
+ } else if (dsh->mode & DPIP_RAW) {
+ /* Wait for data when the buffer is empty and there's no EOF yet */
+ while (dsh->rdbuf->len == 0 && dsh->status != DPIP_EOF)
+ Dpip_dsh_read(dsh, 1);
+ }
+ }
+
+ if (dsh->mode & DPIP_TAG) {
+ /* return a full tag */
+ if ((p = strstr(dsh->rdbuf->str, DPIP_TAG_END))) {
+ ret = dStrndup(dsh->rdbuf->str, p - dsh->rdbuf->str + 3);
+ dStr_erase(dsh->rdbuf, 0, p - dsh->rdbuf->str + 3);
+ if (strstr(ret, DPIP_MODE_SWITCH_TAG))
+ dsh->mode |= DPIP_LAST_TAG;
+ }
+ } else {
+ /* raw mode, return what we have "as is" */
+ if (dsh->rdbuf->len > 0) {
+ ret = dStrndup(dsh->rdbuf->str, dsh->rdbuf->len);
+ dStr_truncate(dsh->rdbuf, 0);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Close this socket for reading and writing.
+ * (flush pending data)
+ */
+void a_Dpip_dsh_close(Dsh *dsh)
+{
+ int st;
+
+ /* flush internal buffer */
+ a_Dpip_dsh_write(dsh, 1, "", 0);
+
+ /* close fds */
+ while((st = close(dsh->fd_in)) < 0 && errno == EINTR) ;
+ if (st < 0)
+ MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno));
+ if (dsh->fd_out != dsh->fd_in) {
+ while((st = close(dsh->fd_out)) < 0 && errno == EINTR) ;
+ if (st < 0)
+ MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno));
+ }
+}
+
+/*
+ * Free the SockHandler structure
+ */
+void a_Dpip_dsh_free(Dsh *dsh)
+{
+ dReturn_if (dsh == NULL);
+
+ dStr_free(dsh->wrbuf, 1);
+ dStr_free(dsh->rdbuf, 1);
+ dFree(dsh);
+}
diff --git a/dpip/dpip.h b/dpip/dpip.h
index 29bb5fcf..083732ff 100644
--- a/dpip/dpip.h
+++ b/dpip/dpip.h
@@ -9,6 +9,40 @@
extern "C" {
#endif /* __cplusplus */
+#include "../dlib/dlib.h"
+
+/*
+ * Communication mode flags
+ */
+#define DPIP_TAG 1 /* Dpip tags in the socket */
+#define DPIP_LAST_TAG 2 /* Dpip mode-switching tag */
+#define DPIP_RAW 4 /* Raw data in the socket */
+#define DPIP_NONBLOCK 8 /* Nonblocking IO */
+
+typedef enum {
+ DPIP_EAGAIN,
+ DPIP_ERROR,
+ DPIP_EOF
+} DpipDshStatus;
+
+/*
+ * Dpip socket handler type.
+ */
+typedef struct _DpipSocketHandler Dsh;
+struct _DpipSocketHandler {
+ int fd_in;
+ int fd_out;
+ /* FILE *in; --Unused. The stream functions block when reading. */
+ FILE *out;
+
+ Dstr *wrbuf; /* write buffer */
+ Dstr *rdbuf; /* read buffer */
+ int flush_sz; /* max size before flush */
+
+ int mode; /* mode flags: DPIP_TAG | DPIP_LAST_TAG | DPIP_RAW */
+ int status; /* status code: DPIP_EAGAIN | DPIP_ERROR | DPIP_EOF */
+};
+
/*
* Printf like function for building dpip commands.
@@ -23,8 +57,30 @@ char *a_Dpip_build_cmd(const char *format, ...);
* (dpip character escaping is removed here)
* Return value: the attribute value, or NULL if not present or malformed.
*/
-char *a_Dpip_get_attr(char *tag, size_t tagsize, const char *attrname);
+char *a_Dpip_get_attr(const char *tag, const char *attrname);
+char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname);
+
+int a_Dpip_check_auth(const char *auth);
+
+/*
+ * Dpip socket API
+ */
+Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz);
+int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize);
+int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str);
+int a_Dpip_dsh_tryflush(Dsh *dsh);
+int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize);
+char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking);
+void a_Dpip_dsh_close(Dsh *dsh);
+void a_Dpip_dsh_free(Dsh *dsh);
+#define a_Dpip_dsh_printf(sh, flush, ...) \
+ D_STMT_START { \
+ Dstr *dstr = dStr_sized_new(128); \
+ dStr_sprintf(dstr, __VA_ARGS__); \
+ a_Dpip_dsh_write(sh, flush, dstr->str, dstr->len); \
+ dStr_free(dstr, 1); \
+ } D_STMT_END
#ifdef __cplusplus
}
diff --git a/dw/alignedtextblock.cc b/dw/alignedtextblock.cc
index bb24a3bc..dde408b2 100644
--- a/dw/alignedtextblock.cc
+++ b/dw/alignedtextblock.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -27,8 +26,8 @@ namespace dw {
AlignedTextblock::List::List ()
{
- textblocks = new misc::SimpleVector <AlignedTextblock*> (4);
- values = new misc::SimpleVector <int> (4);
+ textblocks = new lout::misc::SimpleVector <AlignedTextblock*> (4);
+ values = new lout::misc::SimpleVector <int> (4);
maxValue = 0;
refCount = 0;
}
@@ -54,7 +53,7 @@ void AlignedTextblock::List::unref(int pos)
textblocks->set (pos, NULL);
refCount--;
- if(refCount == 0)
+ if (refCount == 0)
delete this;
}
@@ -63,12 +62,12 @@ int AlignedTextblock::CLASS_ID = -1;
AlignedTextblock::AlignedTextblock (bool limitTextWidth):
Textblock (limitTextWidth)
{
- registerName ("dw::AlignedTextblock", &CLASS_ID);
+ registerName ("dw::AlignedTextblock", &CLASS_ID);
}
void AlignedTextblock::setRefTextblock (AlignedTextblock *ref)
{
- if(ref == NULL)
+ if (ref == NULL)
list = new List();
else
list = ref->list;
@@ -76,7 +75,7 @@ void AlignedTextblock::setRefTextblock (AlignedTextblock *ref)
listPos = list->add (this);
updateValue ();
}
-
+
AlignedTextblock::~AlignedTextblock()
{
list->unref (listPos);
@@ -86,7 +85,7 @@ void AlignedTextblock::updateValue ()
{
if (list) {
list->setValue (listPos, getValue ());
-
+
if (list->getValue (listPos) > list->getMaxValue ()) {
// New value greater than current maximum -> apply it to others.
list->setMaxValue (list->getValue (listPos));
diff --git a/dw/alignedtextblock.hh b/dw/alignedtextblock.hh
index e855f1cc..7bac15bb 100644
--- a/dw/alignedtextblock.hh
+++ b/dw/alignedtextblock.hh
@@ -16,8 +16,8 @@ private:
class List
{
private:
- misc::SimpleVector <AlignedTextblock*> *textblocks;
- misc::SimpleVector <int> *values;
+ lout::misc::SimpleVector <AlignedTextblock*> *textblocks;
+ lout::misc::SimpleVector <int> *values;
int maxValue, refCount;
~List ();
diff --git a/dw/bullet.cc b/dw/bullet.cc
index cc13867c..af7f5451 100644
--- a/dw/bullet.cc
+++ b/dw/bullet.cc
@@ -14,14 +14,13 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "bullet.hh"
-
+
#include <stdio.h>
namespace dw {
@@ -58,7 +57,7 @@ void Bullet::draw (core::View *view, core::Rectangle *area)
case core::style::LIST_STYLE_TYPE_DISC:
default:
view->drawArc (getStyle()->color, core::style::Color::SHADING_NORMAL,
- filled, x, y, l, l, 0, 360);
+ filled, x + l/2, y + l/2, l, l, 0, 360);
}
}
diff --git a/dw/bullet.hh b/dw/bullet.hh
index 00912bd8..98854abb 100644
--- a/dw/bullet.hh
+++ b/dw/bullet.hh
@@ -15,8 +15,8 @@ class Bullet: public core::Widget
{
protected:
void sizeRequestImpl (core::Requisition *requisition);
- void draw (core::View *view, core::Rectangle *area);
- core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ void draw (core::View *view, core::Rectangle *area);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
public:
Bullet ();
diff --git a/dw/events.hh b/dw/events.hh
index 860472ab..c29cb7f4 100644
--- a/dw/events.hh
+++ b/dw/events.hh
@@ -9,7 +9,7 @@ namespace dw {
namespace core {
/**
- * \brief Platform independant representation.
+ * \brief Platform independent representation.
*/
enum ButtonState
{
@@ -25,10 +25,10 @@ enum ButtonState
/**
* \brief Base class for all events.
*
- * The dw::core::Event hierarchy describes events in a platform independant
+ * The dw::core::Event hierarchy describes events in a platform independent
* way.
*/
-class Event: public object::Object
+class Event: public lout::object::Object
{
public:
};
diff --git a/dw/findtext.cc b/dw/findtext.cc
index 15de9f16..f3e0ba20 100644
--- a/dw/findtext.cc
+++ b/dw/findtext.cc
@@ -14,13 +14,13 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core.hh"
+#include "../lout/msg.h"
namespace dw {
namespace core {
@@ -66,14 +66,15 @@ void FindtextState::setWidget (Widget *widget)
hlIterator = NULL;
}
-FindtextState::Result FindtextState::search (const char *key, bool caseSens)
+FindtextState::Result FindtextState::search (const char *key, bool caseSens,
+ bool backwards)
{
if (!widget || *key == 0) // empty keys are not found
return NOT_FOUND;
bool wasHighlighted = unhighlight ();
bool newKey;
-
+
// If the key (or the widget) changes (including case sensitivity),
// the search is started from the beginning.
if (this->key == NULL || this->caseSens != caseSens ||
@@ -86,18 +87,25 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens)
if (nexttab)
delete[] nexttab;
- nexttab = createNexttab (key, caseSens);
-
+ nexttab = createNexttab (key, caseSens, backwards);
+
if (iterator)
delete iterator;
iterator = new CharIterator (widget);
- iterator->next ();
+
+ if (backwards) {
+ /* Go to end */
+ while (iterator->next () ) ;
+ iterator->prev (); //We don't want to be at CharIterator::END.
+ } else {
+ iterator->next ();
+ }
} else
newKey = false;
bool firstTrial = !wasHighlighted || newKey;
- if (search0 ()) {
+ if (search0 (backwards, firstTrial)) {
// Highlighlighting is done with a clone.
hlIterator = iterator->cloneCharIterator ();
for (int i = 0; key[i]; i++)
@@ -110,16 +118,21 @@ FindtextState::Result FindtextState::search (const char *key, bool caseSens)
iterator->next ();
return SUCCESS;
} else {
- if (firstTrial)
+ if (firstTrial) {
return NOT_FOUND;
- else {
+ } else {
// Nothing found anymore, reset the state for the next trial.
delete iterator;
iterator = new CharIterator (widget);
- iterator->next ();
-
+ if (backwards) {
+ /* Go to end */
+ while (iterator->next ()) ;
+ iterator->prev (); //We don't want to be at CharIterator::END.
+ } else {
+ iterator->next ();
+ }
// We expect a success.
- Result result2 = search (key, caseSens);
+ Result result2 = search (key, caseSens, backwards);
assert (result2 == SUCCESS);
return RESTART;
}
@@ -135,11 +148,32 @@ void FindtextState::resetSearch ()
if (key)
delete key;
- key = NULL;
+ key = NULL;
+}
+
+/*
+ * Return a new string: with the reverse of the original.
+ */
+const char* FindtextState::rev(const char *str)
+{
+ if (!str)
+ return NULL;
+
+ int len = strlen(str);
+ char *nstr = new char[len+1];
+ for (int i = 0; i < len; ++i)
+ nstr[i] = str[len-1 -i];
+ nstr[len] = 0;
+
+ return nstr;
}
-int *FindtextState::createNexttab (const char *key, bool caseSens)
+int *FindtextState::createNexttab (const char *needle, bool caseSens,
+ bool backwards)
{
+ const char* key;
+
+ key = (backwards) ? rev(needle) : needle;
int i = 0;
int j = -1;
int l = strlen (key);
@@ -156,6 +190,9 @@ int *FindtextState::createNexttab (const char *key, bool caseSens)
j = nexttab[j];
} while (i < l - 1);
+ if (backwards)
+ delete [] key;
+
return nexttab;
}
@@ -179,30 +216,76 @@ bool FindtextState::unhighlight ()
return false;
}
-bool FindtextState::search0 ()
+bool FindtextState::search0 (bool backwards, bool firstTrial)
{
if (iterator->getChar () == CharIterator::END)
return false;
+ bool ret = false;
+ const char* searchKey = (backwards) ? rev(key) : key;
int j = 0;
bool nextit = true;
int l = strlen (key);
+ if (backwards && !firstTrial) {
+ _MSG("Having to do.");
+ /* Position correctly */
+ /* In order to achieve good results (i.e: find a word that ends within
+ * the previously searched word's limit) we have to position the
+ * iterator in the semilast character of the previously searched word.
+ *
+ * Since we know that if a word was found before it was exactly the
+ * same word as the one we are searching for now, we can apply the
+ * following expression:
+ *
+ * Where l=length of the key and n=num of positions to move:
+ *
+ * n = l - 3
+ *
+ * If n is negative, we have to move backwards, but if it is
+ * positive, we have to move forward. So, when l>=4, we start moving
+ * the iterator forward. */
+
+ if (l==1) {
+ iterator->prev();
+ iterator->prev();
+ } else if (l==2) {
+ iterator->prev();
+ } else if (l>=4) {
+ for (int i=0; i<l-3; i++) {
+ iterator->next();
+ }
+ }
+
+ } else if (backwards && l==1) {
+ /* Particular case where we can't find the last character */
+ iterator->next();
+ }
+
do {
- if (j == -1 || charsEqual (iterator->getChar (), key[j], caseSens)) {
+ if (j == -1 || charsEqual (iterator->getChar(),searchKey[j],caseSens)) {
j++;
- nextit = iterator->next ();
+ nextit = backwards ? iterator->prev () : iterator->next ();
} else
j = nexttab[j];
} while (nextit && j < l);
-
+
if (j >= l) {
- // Go back to where the word was found.
- for (int i = 0; i < l; i++)
- iterator->prev ();
- return true;
- } else
- return false;
+ if (backwards) {
+ //This is the location of the key
+ iterator->next();
+ } else {
+ // Go back to where the key was found.
+ for (int i = 0; i < l; i++)
+ iterator->prev ();
+ }
+ ret = true;
+ }
+
+ if (backwards)
+ delete [] searchKey;
+
+ return ret;
}
} // namespace dw
diff --git a/dw/findtext.hh b/dw/findtext.hh
index d0c20206..14789878 100644
--- a/dw/findtext.hh
+++ b/dw/findtext.hh
@@ -14,12 +14,12 @@ class FindtextState
{
public:
typedef enum {
- /** \brief The next occurance of the pattern has been found. */
+ /** \brief The next occurrence of the pattern has been found. */
SUCCESS,
/**
- * \brief There is no further occurance of the pattern, instead, the
- * first occurance has been selected.
+ * \brief There is no further occurrence of the pattern, instead, the
+ * first occurrence has been selected.
*/
RESTART,
@@ -48,31 +48,33 @@ private:
* dw::core::Findtext::widget
*/
Widget *widget;
-
+
/** \brief The position from where the next search will start. */
CharIterator *iterator;
/**
* \brief The position from where the characters are highlighted.
- *
+ *
* NULL, when no text is highlighted.
*/
- CharIterator *hlIterator;
+ CharIterator *hlIterator;
+
+ static const char* rev(const char* _str); /* reverse a C string */
- static int *createNexttab (const char *key, bool caseSens);
+ static int *createNexttab (const char *needle,bool caseSens,bool backwards);
bool unhighlight ();
- bool search0 ();
+ bool search0 (bool backwards, bool firstTrial);
inline static bool charsEqual (char c1, char c2, bool caseSens)
{ return caseSens ? c1 == c2 : tolower (c1) == tolower (c2) ||
- isspace (c1) && isspace (c2); }
+ (isspace (c1) && isspace (c2)); }
public:
FindtextState ();
~FindtextState ();
void setWidget (Widget *widget);
- Result search (const char *key, bool caseSens);
+ Result search (const char *key, bool caseSens, bool backwards);
void resetSearch ();
};
diff --git a/dw/fltkcomplexbutton.cc b/dw/fltkcomplexbutton.cc
index 6c15cee3..f80d0eb0 100644
--- a/dw/fltkcomplexbutton.cc
+++ b/dw/fltkcomplexbutton.cc
@@ -1,21 +1,22 @@
-//
-//
-// Copyright 1998-2006 by Bill Spitzak and others.
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Library General Public
-// License as published by the Free Software Foundation; either
-// version 3 of the License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Library General Public License for more details.
-//
-// You should have received a copy of the GNU Library General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-// USA.
+
+// fltkcomplexbutton.cc contains code from FLTK2's src/Button.cxx
+// that is Copyright 1998-2006 by Bill Spitzak and others.
+// (see http://svn.easysw.com/public/fltk/fltk/trunk/src/Button.cxx)
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
#include <fltk/events.h>
#include <fltk/damage.h>
@@ -87,7 +88,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) {
if (pushed()) return 1; // ignore extra pushes on currently-pushed button
initial_state = state();
clear_flag(PUSHED);
- do_callback();
+ /* do_callback(); */
case DRAG: {
bool inside = event_inside(rectangle);
if (inside) {
@@ -112,7 +113,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) {
redraw(DAMAGE_VALUE);
if (type() == RADIO)
setonly();
- else if (type()) // TOGGLE
+ else if (type() == TOGGLE)
state(!initial_state);
else {
state(initial_state);
@@ -139,7 +140,7 @@ int ComplexButton::handle(int event, const Rectangle& rectangle) {
setonly();
if (when() & WHEN_CHANGED) do_callback(); else set_changed();
}
- } else if (type()) { // TOGGLE
+ } else if (type() == TOGGLE) {
state(!state());
if (when() & WHEN_CHANGED) do_callback(); else set_changed();
}
diff --git a/dw/fltkcomplexbutton.hh b/dw/fltkcomplexbutton.hh
index 72799d42..83160c06 100644
--- a/dw/fltkcomplexbutton.hh
+++ b/dw/fltkcomplexbutton.hh
@@ -1,22 +1,22 @@
-//
-//
-// Copyright 2002 by Bill Spitzak and others.
-//
-// This library is free software; you can redistribute it and/or
-// modify it under the terms of the GNU Library General Public
-// License as published by the Free Software Foundation; either
-// version 2 of the License, or (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Library General Public License for more details.
-//
-// You should have received a copy of the GNU Library General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
-// USA.
-//
+
+// fltkcomplexbutton.hh contains code from FLTK2's fltk/Button.h
+// that is Copyright 2002 by Bill Spitzak and others.
+// (see http://svn.easysw.com/public/fltk/fltk/trunk/fltk/Button.h)
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
#ifndef __FLTK_COMPLEX_BUTTON_HH__
#define __FLTK_COMPLEX_BUTTON_HH__
diff --git a/dw/fltkflatview.cc b/dw/fltkflatview.cc
index 7f5f88a6..85385394 100644
--- a/dw/fltkflatview.cc
+++ b/dw/fltkflatview.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -52,7 +51,7 @@ void FltkFlatView::setCanvasSize (int width, int ascent, int descent)
#if 0
FltkWidgetView::setCanvasSize (width, ascent, descent);
-
+
w (width);
h (ascent + descent);
#endif
diff --git a/dw/fltkflatview.hh b/dw/fltkflatview.hh
index dee5498f..5106cbdf 100644
--- a/dw/fltkflatview.hh
+++ b/dw/fltkflatview.hh
@@ -22,7 +22,7 @@ protected:
public:
FltkFlatView (int x, int y, int w, int h, const char *label = 0);
~FltkFlatView ();
-
+
void setCanvasSize (int width, int ascent, int descent);
bool usesViewport ();
diff --git a/dw/fltkimgbuf.cc b/dw/fltkimgbuf.cc
index b8beb1cb..6ba7ff1d 100644
--- a/dw/fltkimgbuf.cc
+++ b/dw/fltkimgbuf.cc
@@ -14,34 +14,36 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "fltkcore.hh"
+#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <fltk/draw.h>
#include <fltk/Color.h>
+#define IMAGE_MAX_AREA (6000 * 6000)
+
using namespace fltk;
namespace dw {
namespace fltk {
-using namespace container::typed;
+using namespace lout::container::typed;
FltkImgbuf::FltkImgbuf (Type type, int width, int height)
{
- //printf("FltkImgbuf: new root %p\n", this);
+ _MSG("FltkImgbuf: new root %p\n", this);
init (type, width, height, NULL);
}
FltkImgbuf::FltkImgbuf (Type type, int width, int height, FltkImgbuf *root)
{
- //printf("FltkImgbuf: new scaled %p, root is %p\n", this, root);
+ _MSG("FltkImgbuf: new scaled %p, root is %p\n", this, root);
init (type, width, height, root);
}
@@ -58,19 +60,18 @@ void FltkImgbuf::init (Type type, int width, int height, FltkImgbuf *root)
case RGB: bpp = 3; break;
default: bpp = 1; break;
}
- //fprintf(stderr,"FltkImgbuf::init width=%d height=%d bpp=%d\n",
- // width, height, bpp);
+ _MSG("FltkImgbuf::init width=%d height=%d bpp=%d\n", width, height, bpp);
rawdata = new uchar[bpp * width * height];
// Set light-gray as interim background color.
memset(rawdata, 222, width*height*bpp);
refCount = 1;
deleteOnUnref = true;
- copiedRows = new misc::BitSet (height);
+ copiedRows = new lout::misc::BitSet (height);
// The list is only used for root buffers.
if (isRoot())
- scaledBuffers = new container::typed::List <FltkImgbuf> (true);
+ scaledBuffers = new lout::container::typed::List <FltkImgbuf> (true);
else
scaledBuffers = NULL;
@@ -85,12 +86,7 @@ void FltkImgbuf::init (Type type, int width, int height, FltkImgbuf *root)
FltkImgbuf::~FltkImgbuf ()
{
- //printf ("FltkImgbuf::~FltkImgbuf (%s)\n", isRoot() ? "root" : "scaled");
-
- //if (root)
- // printf("FltkImgbuf[scaled %p, root is %p]: deleted\n", this, root);
- //else
- // printf("FltkImgbuf[root %p]: deleted\n", this);
+ _MSG("~FltkImgbuf[%s %p] deleted\n", isRoot() ? "root":"scaled", this);
if (!isRoot())
root->detachScaledBuf (this);
@@ -110,8 +106,8 @@ void FltkImgbuf::detachScaledBuf (FltkImgbuf *scaledBuf)
{
scaledBuffers->detachRef (scaledBuf);
- //printf("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n",
- // this, scaledBuf, scaledBuffers->size ());
+ _MSG("FltkImgbuf[root %p]: scaled buffer %p is detached, %d left\n",
+ this, scaledBuf, scaledBuffers->size ());
if (refCount == 0 && scaledBuffers->isEmpty () && deleteOnUnref)
// If the root buffer is not used anymore, but this is the last scaled
@@ -128,17 +124,17 @@ inline void FltkImgbuf::scaleRow (int row, const core::byte *data)
{
int sr1 = scaledY (row);
int sr2 = scaledY (row + 1);
-
- for(int sr = sr1; sr < sr2; sr++) {
+
+ for (int sr = sr1; sr < sr2; sr++) {
// Avoid multiple passes.
if (copiedRows->get(sr)) continue;
copiedRows->set (sr, true);
if (sr == sr1) {
- for(int px = 0; px < root->width; px++) {
+ for (int px = 0; px < root->width; px++) {
int px1 = px * width / root->width;
int px2 = (px+1) * width / root->width;
- for(int sp = px1; sp < px2; sp++) {
+ for (int sp = px1; sp < px2; sp++) {
memcpy(rawdata + (sr*width + sp)*bpp, data + px*bpp, bpp);
}
}
@@ -168,16 +164,16 @@ void FltkImgbuf::newScan ()
if (isRoot()) {
for (Iterator<FltkImgbuf> it = scaledBuffers->iterator(); it.hasNext();){
FltkImgbuf *sb = it.getNext ();
- sb->copiedRows->clear();
+ sb->copiedRows->clear();
}
}
}
core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height)
{
- if (root)
+ if (!isRoot())
return root->getScaledBuf (width, height);
-
+
if (width == this->width && height == this->height) {
ref ();
return this;
@@ -191,6 +187,18 @@ core::Imgbuf* FltkImgbuf::getScaledBuf (int width, int height)
}
}
+ /* Check for excessive image sizes which would cause crashes due to
+ * too big allocations for the image buffer.
+ * In this case we return a pointer to the unscaled image buffer.
+ */
+ if (width <= 0 || height <= 0 ||
+ width > IMAGE_MAX_AREA / height) {
+ MSG("FltkImgbuf::getScaledBuf: suspicious image size request %dx%d\n",
+ width, height);
+ ref ();
+ return this;
+ }
+
/* This size is not yet used, so a new buffer has to be created. */
FltkImgbuf *sb = new FltkImgbuf (type, width, height, this);
scaledBuffers->append (sb);
@@ -207,8 +215,8 @@ void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area)
area->y = row;
area->width = width;
area->height = 1;
- //fprintf(stderr,"::getRowArea: area x=%d y=%d width=%d height=%d\n",
- // area->x, area->y, area->width, area->height);
+ _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
+ area->x, area->y, area->width, area->height);
} else {
// scaled buffer
int sr1 = scaledY (row);
@@ -218,8 +226,8 @@ void FltkImgbuf::getRowArea (int row, dw::core::Rectangle *area)
area->y = sr1;
area->width = width;
area->height = sr2 - sr1;
- //fprintf(stderr,"::getRowArea: area x=%d y=%d width=%d height=%d\n",
- // area->x, area->y, area->width, area->height);
+ _MSG("::getRowArea: area x=%d y=%d width=%d height=%d\n",
+ area->x, area->y, area->width, area->height);
}
}
@@ -238,28 +246,30 @@ void FltkImgbuf::ref ()
refCount++;
//if (root)
- // printf("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
- // this, root, refCount);
+ // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
+ // this, root, refCount);
//else
- // printf("FltkImgbuf[root %p]: ref() => %d\n", this, refCount);
+ // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount);
}
void FltkImgbuf::unref ()
{
//if (root)
- // printf("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
- // this, root, refCount - 1);
+ // MSG("FltkImgbuf[scaled %p, root is %p]: ref() => %d\n",
+ // this, root, refCount - 1);
//else
- // printf("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1);
+ // MSG("FltkImgbuf[root %p]: ref() => %d\n", this, refCount - 1);
if (--refCount == 0) {
if (isRoot ()) {
// Root buffer, it must be ensured that no scaled buffers are left.
// See also FltkImgbuf::detachScaledBuf().
- if (scaledBuffers->isEmpty () && deleteOnUnref)
+ if (scaledBuffers->isEmpty () && deleteOnUnref) {
delete this;
- else
- printf("FltkImgbuf[root %p]: not deleted\n", this);
+ } else {
+ _MSG("FltkImgbuf[root %p]: not deleted. numScaled=%d\n",
+ this, scaledBuffers->size ());
+ }
} else
// Scaled buffer buffer, simply delete it.
delete this;
@@ -268,7 +278,7 @@ void FltkImgbuf::unref ()
bool FltkImgbuf::lastReference ()
{
- return refCount == 1 &&
+ return refCount == 1 &&
(scaledBuffers == NULL || scaledBuffers->isEmpty ());
}
@@ -293,29 +303,15 @@ int FltkImgbuf::scaledY(int ySrc)
}
void FltkImgbuf::draw (::fltk::Widget *target, int xRoot, int yRoot,
- int x, int y, int width, int height)
+ int x, int y, int width, int height)
{
- // TODO (i): Implementation.
- // TODO (ii): Clarify the question, whether "target" is the current widget
- // (and so has not to be passed at all).
+ // TODO: Clarify the question, whether "target" is the current widget
+ // (and so has not to be passed at all).
-/*
- setcolor (0);
+ _MSG("::draw: xRoot=%d x=%d yRoot=%d y=%d width=%d height=%d\n"
+ " this->width=%d this->height=%d\n",
+ xRoot, x, yRoot, y, width, height, this->width, this->height);
- for (int row = y; row < y + height; row++) {
- if (copiedRows->get (row)) {
- ::fltk::Rectangle rect (x + xRoot, row + yRoot, width, 1);
- fillrect (rect);
- }
- }
-*/
-
- //fprintf(stderr,"::draw: xRoot=%d x=%d yRoot=%d y=%d width=%d height=%d\n"
- // " this->width=%d this->height=%d\n",
- // xRoot, x, yRoot, y, width, height, this->width, this->height);
-
-//{
-#if 1
if (x > this->width || y > this->height) {
return;
}
@@ -328,19 +324,10 @@ void FltkImgbuf::draw (::fltk::Widget *target, int xRoot, int yRoot,
height = this->height - y;
}
- // almost OK for rows. For some unknown reason it trims the bottom and
- // rightmost parts when scrolling.
+ // Draw
::fltk::Rectangle rect (xRoot + x, yRoot + y, width, height);
PixelType ptype = (type == RGBA) ? ::fltk::RGBA : ::fltk::RGB;
drawimage(rawdata+bpp*(y*this->width + x),ptype,rect,bpp*this->width);
-
-#else
- // OK for full image.
- ::fltk::Rectangle rect (xRoot, yRoot, this->width, this->height);
- PixelType ptype = (type == RGBA) ? ::fltk::RGBA : ::fltk::RGB;
- drawimage(rawdata,ptype,rect);
-#endif
-//}
}
} // namespace dw
diff --git a/dw/fltkmisc.cc b/dw/fltkmisc.cc
index 08d75854..5d20a87a 100644
--- a/dw/fltkmisc.cc
+++ b/dw/fltkmisc.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
diff --git a/dw/fltkplatform.cc b/dw/fltkplatform.cc
index 337f4dba..97e10710 100644
--- a/dw/fltkplatform.cc
+++ b/dw/fltkplatform.cc
@@ -14,17 +14,19 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
+#include "../lout/msg.h"
#include "fltkcore.hh"
#include <fltk/draw.h>
#include <fltk/run.h>
#include <fltk/events.h>
+#include <fltk/Monitor.h>
+#include <fltk/InvisibleBox.h>
+#include <fltk/Tooltip.h>
#include <fltk/utf.h>
#include <stdio.h>
@@ -32,6 +34,7 @@ namespace dw {
namespace fltk {
using namespace ::fltk;
+using namespace lout;
/**
* \todo Distinction between italics and oblique would be nice.
@@ -47,23 +50,22 @@ FltkFont::FltkFont (core::style::FontAttrs *attrs)
copyAttrs (attrs);
int fa = 0;
- if(weight >= 500)
+ if (weight >= 500)
fa |= BOLD;
- if(style != core::style::FONT_STYLE_NORMAL)
+ if (style != core::style::FONT_STYLE_NORMAL)
fa |= ITALIC;
font = ::fltk::font(name, fa);
- if(font == NULL) {
- fprintf(stderr, "No font '%s', using default sans-serif font.\n", name);
+ if (font == NULL) {
/*
* If using xft, fltk::HELVETICA just means sans, fltk::COURIER
* means mono, and fltk::TIMES means serif.
*/
- font = HELVETICA;
- }
+ font = HELVETICA->plus (fa);
+ }
setfont(font, size);
- spaceWidth = (int)getwidth(" ");
+ spaceWidth = misc::max(0, (int)getwidth(" ") + letterSpacing);
int xw, xh;
measure("x", xw, xh);
xHeight = xh;
@@ -81,6 +83,12 @@ FltkFont::~FltkFont ()
fontsTable->remove (this);
}
+bool
+FltkPlatform::fontExists (const char *name)
+{
+ return ::fltk::font(name) != NULL;
+}
+
FltkFont*
FltkFont::create (core::style::FontAttrs *attrs)
{
@@ -100,11 +108,9 @@ container::typed::HashTable <dw::core::style::ColorAttrs,
new container::typed::HashTable <dw::core::style::ColorAttrs,
FltkColor> (false, false);
-FltkColor::FltkColor (int color, core::style::Color::Type type):
- Color (color, type)
+FltkColor::FltkColor (int color): Color (color)
{
this->color = color;
- this->type = type;
/*
* fltk/setcolor.cxx:
@@ -122,13 +128,10 @@ FltkColor::FltkColor (int color, core::style::Color::Type type):
colors[SHADING_NORMAL] = ::fltk::BLACK;
if (!(colors[SHADING_INVERSE] = shadeColor (color, SHADING_INVERSE) << 8))
colors[SHADING_INVERSE] = ::fltk::BLACK;
-
- if(type == core::style::Color::TYPE_SHADED) {
- if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8))
- colors[SHADING_DARK] = ::fltk::BLACK;
- if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8))
- colors[SHADING_LIGHT] = ::fltk::BLACK;
- }
+ if (!(colors[SHADING_DARK] = shadeColor (color, SHADING_DARK) << 8))
+ colors[SHADING_DARK] = ::fltk::BLACK;
+ if (!(colors[SHADING_LIGHT] = shadeColor (color, SHADING_LIGHT) << 8))
+ colors[SHADING_LIGHT] = ::fltk::BLACK;
}
FltkColor::~FltkColor ()
@@ -136,17 +139,74 @@ FltkColor::~FltkColor ()
colorsTable->remove (this);
}
-FltkColor * FltkColor::create (int col, core::style::Color::Type type)
+FltkColor * FltkColor::create (int col)
{
- ColorAttrs attrs(col, type);
+ ColorAttrs attrs(col);
FltkColor *color = colorsTable->get (&attrs);
if (color == NULL) {
- color = new FltkColor (col, type);
+ color = new FltkColor (col);
colorsTable->put (color, color);
}
- return color;
+ return color;
+}
+
+FltkTooltip::FltkTooltip (const char *text) : Tooltip(text)
+{
+ shown = false;
+
+ if (!text || !strpbrk(text, "&@")) {
+ escaped_str = NULL;
+ } else {
+ /*
+ * WORKAROUND: ::fltk::Tooltip::tooltip_timeout() makes instance_
+ * if necessary, and immediately uses it. This means that we can't
+ * get our hands on it to set RAW_LABEL until after it has been shown
+ * once. So let's escape the special characters ourselves.
+ */
+ const char *src = text;
+ char *dest = escaped_str = (char *) malloc(strlen(text) * 2 + 1);
+
+ while (*src) {
+ if (*src == '&' || *src == '@')
+ *dest++ = *src;
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+ }
+}
+
+FltkTooltip::~FltkTooltip ()
+{
+ if (shown)
+ ::fltk::Tooltip::exit();
+ if (escaped_str)
+ free(escaped_str);
+}
+
+FltkTooltip *FltkTooltip::create (const char *text)
+{
+ return new FltkTooltip(text);
+}
+
+void FltkTooltip::onEnter()
+{
+ fltk::Widget *widget = fltk::belowmouse();
+
+ ::fltk::Tooltip::enter(widget, *((fltk::Rectangle *)widget),
+ escaped_str ? escaped_str : str);
+ shown = true;
+}
+
+void FltkTooltip::onLeave()
+{
+ ::fltk::Tooltip::exit();
+ shown = false;
+}
+
+void FltkTooltip::onMotion()
+{
}
void FltkView::addFltkWidget (::fltk::Widget *widget,
@@ -187,9 +247,9 @@ core::ui::ListResource *
FltkPlatform::FltkResourceFactory::createListResource (core::ui
::ListResource
::SelectionMode
- selectionMode)
+ selectionMode, int rows)
{
- return new ui::FltkListResource (platform, selectionMode);
+ return new ui::FltkListResource (platform, selectionMode, rows);
}
core::ui::OptionMenuResource *
@@ -200,9 +260,10 @@ FltkPlatform::FltkResourceFactory::createOptionMenuResource ()
core::ui::EntryResource *
FltkPlatform::FltkResourceFactory::createEntryResource (int maxLength,
- bool password)
+ bool password,
+ const char *label)
{
- return new ui::FltkEntryResource (platform, maxLength, password);
+ return new ui::FltkEntryResource (platform, maxLength, password, label);
}
core::ui::MultiLineTextResource *
@@ -238,7 +299,7 @@ FltkPlatform::FltkPlatform ()
idleFuncRunning = false;
idleFuncId = 0;
- views = new container::typed::List <FltkView> (false);
+ view = NULL;
resources = new container::typed::List <ui::FltkResource> (false);
resourceFactory.setPlatform (this);
@@ -246,10 +307,9 @@ FltkPlatform::FltkPlatform ()
FltkPlatform::~FltkPlatform ()
{
- if(idleFuncRunning)
+ if (idleFuncRunning)
remove_idle (generalStaticIdle, (void*)this);
delete idleQueue;
- delete views;
delete resources;
}
@@ -261,34 +321,51 @@ void FltkPlatform::setLayout (core::Layout *layout)
void FltkPlatform::attachView (core::View *view)
{
- views->append ((FltkView*)view);
+ if (this->view)
+ MSG_ERR("FltkPlatform::attachView: multiple views!\n");
+ this->view = (FltkView*)view;
for (container::typed::Iterator <ui::FltkResource> it =
resources->iterator (); it.hasNext (); ) {
ui::FltkResource *resource = it.getNext ();
- resource->attachView ((FltkView*)view);
+ resource->attachView (this->view);
}
}
void FltkPlatform::detachView (core::View *view)
{
- views->removeRef ((FltkView*)view);
+ if (this->view != view)
+ MSG_ERR("FltkPlatform::detachView: this->view: %p view: %p\n",
+ this->view, view);
for (container::typed::Iterator <ui::FltkResource> it =
resources->iterator (); it.hasNext (); ) {
ui::FltkResource *resource = it.getNext ();
resource->detachView ((FltkView*)view);
}
+ this->view = NULL;
}
int FltkPlatform::textWidth (core::style::Font *font, const char *text,
int len)
{
+ int width;
FltkFont *ff = (FltkFont*) font;
setfont (ff->font, ff->size);
- return (int) getwidth (text, len);
+ width = (int) getwidth (text, len);
+
+ if (font->letterSpacing) {
+ int curr = 0, next = 0;
+
+ while (next < len) {
+ next = nextGlyph(text, curr);
+ width += font->letterSpacing;
+ curr = next;
+ }
+ }
+ return width;
}
int FltkPlatform::nextGlyph (const char *text, int idx)
@@ -301,6 +378,16 @@ int FltkPlatform::prevGlyph (const char *text, int idx)
return utf8back (&text[idx - 1], text, &text[strlen (text)]) - text;
}
+float FltkPlatform::dpiX ()
+{
+ return ::fltk::Monitor::all ().dpi_x ();
+}
+
+float FltkPlatform::dpiY ()
+{
+ return ::fltk::Monitor::all ().dpi_y ();
+}
+
void FltkPlatform::generalStaticIdle (void *data)
{
((FltkPlatform*)data)->generalIdle();
@@ -314,12 +401,12 @@ void FltkPlatform::generalIdle ()
/* Execute the first function in the list. */
idleFunc = idleQueue->getFirst ();
(layout->*(idleFunc->func)) ();
-
+
/* Remove this function. */
idleQueue->removeRef(idleFunc);
}
- if(idleQueue->isEmpty()) {
+ if (idleQueue->isEmpty()) {
idleFuncRunning = false;
remove_idle (generalStaticIdle, (void*)this);
}
@@ -334,7 +421,7 @@ int FltkPlatform::addIdle (void (core::Layout::*func) ())
* Since ... (todo) we have to wrap around fltk_add_idle. There is only one
* idle function, the passed idle function is put into a queue.
*/
- if(!idleFuncRunning) {
+ if (!idleFuncRunning) {
add_idle (generalStaticIdle, (void*)this);
idleFuncRunning = true;
}
@@ -355,15 +442,15 @@ void FltkPlatform::removeIdle (int idleId)
container::typed::Iterator <IdleFunc> it;
IdleFunc *idleFunc;
- for(found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) {
+ for (found = false, it = idleQueue->iterator(); !found && it.hasNext(); ) {
idleFunc = it.getNext();
- if(idleFunc->id == idleId) {
+ if (idleFunc->id == idleId) {
idleQueue->removeRef (idleFunc);
found = true;
}
}
- if(idleFuncRunning && idleQueue->isEmpty())
+ if (idleFuncRunning && idleQueue->isEmpty())
remove_idle (generalStaticIdle, (void*)this);
}
@@ -374,14 +461,14 @@ core::style::Font *FltkPlatform::createFont (core::style::FontAttrs
return FltkFont::create (attrs);
}
-core::style::Color *FltkPlatform::createSimpleColor (int color)
+core::style::Color *FltkPlatform::createColor (int color)
{
- return FltkColor::create (color, core::style::Color::TYPE_SIMPLE);
+ return FltkColor::create (color);
}
-core::style::Color *FltkPlatform::createShadedColor (int color)
+core::style::Tooltip *FltkPlatform::createTooltip (const char *text)
{
- return FltkColor::create (color, core::style::Color::TYPE_SHADED);
+ return FltkTooltip::create (text);
}
void FltkPlatform::copySelection(const char *text)
@@ -404,12 +491,7 @@ core::ui::ResourceFactory *FltkPlatform::getResourceFactory ()
void FltkPlatform::attachResource (ui::FltkResource *resource)
{
resources->append (resource);
-
- for (container::typed::Iterator <FltkView> it = views->iterator ();
- it.hasNext (); ) {
- FltkView *view = it.getNext ();
- resource->attachView (view);
- }
+ resource->attachView (view);
}
void FltkPlatform::detachResource (ui::FltkResource *resource)
diff --git a/dw/fltkplatform.hh b/dw/fltkplatform.hh
index 7b3d3e73..7a708938 100644
--- a/dw/fltkplatform.hh
+++ b/dw/fltkplatform.hh
@@ -24,7 +24,7 @@ class FltkFont: public core::style::Font
public:
::fltk::Font *font;
-
+
static FltkFont *create (core::style::FontAttrs *attrs);
};
@@ -34,13 +34,27 @@ class FltkColor: public core::style::Color
static lout::container::typed::HashTable <dw::core::style::ColorAttrs,
FltkColor> *colorsTable;
- FltkColor (int color, core::style::Color::Type type);
+ FltkColor (int color);
~FltkColor ();
public:
int colors[SHADING_NUM];
- static FltkColor *create(int color, core::style::Color::Type type);
+ static FltkColor *create(int color);
+};
+
+class FltkTooltip: public core::style::Tooltip
+{
+private:
+ FltkTooltip (const char *text);
+ ~FltkTooltip ();
+ bool shown;
+ char *escaped_str; /* fltk WORKAROUND */
+public:
+ static FltkTooltip *create(const char *text);
+ void onEnter();
+ void onLeave();
+ void onMotion();
};
@@ -78,10 +92,12 @@ private:
core::ui::ComplexButtonResource *
createComplexButtonResource (core::Widget *widget, bool relief);
core::ui::ListResource *
- createListResource (core::ui::ListResource::SelectionMode selectionMode);
+ createListResource (core::ui::ListResource::SelectionMode selectionMode,
+ int rows);
core::ui::OptionMenuResource *createOptionMenuResource ();
core::ui::EntryResource *createEntryResource (int maxLength,
- bool password);
+ bool password,
+ const char *label);
core::ui::MultiLineTextResource *createMultiLineTextResource (int cols,
int rows);
core::ui::CheckButtonResource *createCheckButtonResource (bool
@@ -109,7 +125,7 @@ private:
static void generalStaticIdle(void *data);
void generalIdle();
- lout::container::typed::List <FltkView> *views;
+ FltkView *view;
lout::container::typed::List <ui::FltkResource> *resources;
public:
@@ -117,23 +133,26 @@ public:
~FltkPlatform ();
void setLayout (core::Layout *layout);
-
+
void attachView (core::View *view);
void detachView (core::View *view);
-
+
int textWidth (core::style::Font *font, const char *text, int len);
int nextGlyph (const char *text, int idx);
int prevGlyph (const char *text, int idx);
-
+ float dpiX ();
+ float dpiY ();
+
int addIdle (void (core::Layout::*func) ());
void removeIdle (int idleId);
-
+
core::style::Font *createFont (core::style::FontAttrs *attrs,
bool tryEverything);
- core::style::Color *createSimpleColor (int color);
- core::style::Color *createShadedColor (int color);
-
+ bool fontExists (const char *name);
+ core::style::Color *createColor (int color);
+ core::style::Tooltip *createTooltip (const char *text);
+
core::Imgbuf *createImgbuf (core::Imgbuf::Type type, int width, int height);
void copySelection(const char *text);
diff --git a/dw/fltkpreview.cc b/dw/fltkpreview.cc
index 6bed7adf..7096420f 100644
--- a/dw/fltkpreview.cc
+++ b/dw/fltkpreview.cc
@@ -14,11 +14,10 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
+#include "../lout/msg.h"
#include "fltkpreview.hh"
#include "fltkmisc.hh"
@@ -104,6 +103,11 @@ void FltkPreview::scrollTo (int x, int y)
scrollY = y;
}
+void FltkPreview::scroll (dw::core::ScrollCommand cmd)
+{
+ MSG_ERR("FltkPreview::scroll not implemented\n");
+}
+
void FltkPreview::setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness)
@@ -155,7 +159,7 @@ bool FltkPreview::usesFltkWidgets ()
return false;
}
-void FltkPreview::drawFltkWidget (Widget *widget,
+void FltkPreview::drawFltkWidget (::fltk::Widget *widget,
core::Rectangle *area)
{
}
@@ -191,7 +195,7 @@ void FltkPreviewWindow::reallocate ()
int mx, my, width, height;
bool warp = false;
- if(preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) {
+ if (preview->canvasHeight * maxWidth > maxHeight * preview->canvasWidth) {
// Expand to maximal height (most likely case).
width = preview->canvasWidth * maxHeight / preview->canvasHeight;
height = maxHeight;
@@ -288,7 +292,7 @@ int FltkPreviewButton::handle (int event)
case RELEASE:
window->hideWindow ();
return Button::handle (event);
-
+
default:
return Button::handle (event);
}
diff --git a/dw/fltkpreview.hh b/dw/fltkpreview.hh
index 2464db89..13db2811 100644
--- a/dw/fltkpreview.hh
+++ b/dw/fltkpreview.hh
@@ -34,6 +34,7 @@ public:
int getHScrollbarThickness ();
int getVScrollbarThickness ();
void scrollTo (int x, int y);
+ void scroll (dw::core::ScrollCommand cmd);
void setViewportSize (int width, int height,
int hScrollbarThickness, int vScrollbarThickness);
diff --git a/dw/fltkui.cc b/dw/fltkui.cc
index 9255d7d2..c1bfd873 100644
--- a/dw/fltkui.cc
+++ b/dw/fltkui.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -23,19 +22,18 @@
#include "fltkcore.hh"
#include "fltkflatview.hh"
#include "fltkcomplexbutton.hh"
+#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <stdio.h>
#include <fltk/Widget.h>
#include <fltk/Group.h>
#include <fltk/Input.h>
-#include <fltk/SecretInput.h>
#include <fltk/TextEditor.h>
#include <fltk/RadioButton.h>
#include <fltk/CheckButton.h>
#include <fltk/Choice.h>
#include <fltk/Browser.h>
-#include <fltk/MultiBrowser.h>
#include <fltk/Font.h>
#include <fltk/draw.h>
#include <fltk/Symbol.h>
@@ -49,8 +47,8 @@ namespace ui {
enum { RELIEF_X_THICKNESS = 3, RELIEF_Y_THICKNESS = 3 };
-using namespace object;
-using namespace container::typed;
+using namespace lout::object;
+using namespace lout::container::typed;
FltkResource::FltkResource (FltkPlatform *platform)
{
@@ -63,6 +61,8 @@ FltkResource::FltkResource (FltkPlatform *platform)
allocation.descent = 0;
style = NULL;
+
+ enabled = true;
}
/**
@@ -71,98 +71,72 @@ FltkResource::FltkResource (FltkPlatform *platform)
*/
void FltkResource::init (FltkPlatform *platform)
{
- viewsAndWidgets = new container::typed::List <ViewAndWidget> (true);
+ view = NULL;
+ widget = NULL;
platform->attachResource (this);
}
FltkResource::~FltkResource ()
{
platform->detachResource (this);
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
-
- if (viewAndWidget->widget) {
- if (viewAndWidget->view) {
- viewAndWidget->view->removeFltkWidget(viewAndWidget->widget);
- }
- delete viewAndWidget->widget;
+ if (widget) {
+ if (view) {
+ view->removeFltkWidget(widget);
}
-
+ delete widget;
}
- delete viewsAndWidgets;
- if(style)
+ if (style)
style->unref ();
}
void FltkResource::attachView (FltkView *view)
{
+ if (this->view)
+ MSG_ERR("FltkResource::attachView: multiple views!\n");
+
if (view->usesFltkWidgets ()) {
- ViewAndWidget *viewAndWidget = new ViewAndWidget();
- viewAndWidget->view = view;
-
- viewAndWidget->widget = createNewWidget (&allocation);
- viewAndWidget->view->addFltkWidget (viewAndWidget->widget, &allocation);
- viewsAndWidgets->append (viewAndWidget);
+ this->view = view;
+
+ widget = createNewWidget (&allocation);
+ view->addFltkWidget (widget, &allocation);
if (style)
- setWidgetStyle (viewAndWidget->widget, style);
+ setWidgetStyle (widget, style);
+ if (! enabled)
+ widget->deactivate ();
}
}
void FltkResource::detachView (FltkView *view)
{
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- if (viewAndWidget->view == view) {
- viewsAndWidgets->removeRef (viewAndWidget);
- return;
- }
- }
-
- fprintf (stderr, "FltkResource::detachView: View not found.");
+ if (this->view != view)
+ MSG_ERR("FltkResource::detachView: this->view: %p view: %p\n",
+ this->view, view);
+ this->view = NULL;
}
void FltkResource::sizeAllocate (core::Allocation *allocation)
{
this->allocation = *allocation;
-
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- viewAndWidget->view->allocateFltkWidget (viewAndWidget->widget,
- allocation);
- }
+ view->allocateFltkWidget (widget, allocation);
}
void FltkResource::draw (core::View *view, core::Rectangle *area)
{
FltkView *fltkView = (FltkView*)view;
- if (fltkView->usesFltkWidgets ()) {
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- if (viewAndWidget->view == fltkView) {
- fltkView->drawFltkWidget (viewAndWidget->widget, area);
- break;
- }
- }
+ if (fltkView->usesFltkWidgets () && this->view == fltkView) {
+ fltkView->drawFltkWidget (widget, area);
}
}
void FltkResource::setStyle (core::style::Style *style)
{
- if(this->style)
+ if (this->style)
this->style->unref ();
this->style = style;
style->ref ();
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- setWidgetStyle (viewAndWidget->widget, style);
- }
+ setWidgetStyle (widget, style);
}
void FltkResource::setWidgetStyle (::fltk::Widget *widget,
@@ -176,39 +150,62 @@ void FltkResource::setWidgetStyle (::fltk::Widget *widget,
FltkColor *bg = (FltkColor*)style->backgroundColor;
if (bg) {
+ int normal_bg = bg->colors[FltkColor::SHADING_NORMAL];
+
if (style->color) {
- /*
- * TODO: if/when CSS is implemented, test whether style->color
- * will consistently provide readable widgets.
- */
- int32_t c = bg->colors[FltkColor::SHADING_NORMAL];
- int r = (c >> 24) & 0xff, g = (c >> 16) & 0xff, b = (c >> 8) & 0xff;
- bool light = (r + g >= 0x150) || (r + g + b >= 0x180);
-
- widget->labelcolor(light? ::fltk::BLACK : ::fltk::WHITE);
- widget->textcolor(light? ::fltk::BLACK : ::fltk::WHITE);
- widget->selection_color(light? ::fltk::BLACK : ::fltk::WHITE);
+ int style_fg = ((FltkColor*)style->color)->colors
+ [FltkColor::SHADING_NORMAL];
+ ::fltk::Color fg = ::fltk::contrast(style_fg, normal_bg);
+
+ widget->labelcolor(fg);
+ widget->textcolor(fg);
+ widget->selection_color(fg);
}
- widget->color(bg->colors[FltkColor::SHADING_NORMAL]);
- widget->buttoncolor(bg->colors[FltkColor::SHADING_NORMAL]);
- widget->selection_textcolor(bg->colors[FltkColor::SHADING_NORMAL]);
- if (!(widget->type() & (::fltk::Widget::RADIO|::fltk::Widget::TOGGLE))) {
+ widget->color(normal_bg);
+ widget->buttoncolor(normal_bg);
+ widget->selection_textcolor(normal_bg);
+ if (widget->type() != ::fltk::Widget::RADIO &&
+ widget->type() != ::fltk::Widget::TOGGLE) {
/* it looks awful to highlight the buttons */
widget->highlight_color(bg->colors[FltkColor::SHADING_LIGHT]);
}
}
}
-
+
+void FltkResource::setDisplayed(bool displayed)
+{
+ if (displayed)
+ widget->show();
+ else
+ widget->hide();
+}
+
+bool FltkResource::displayed()
+{
+ bool ret = false;
+
+ if (widget) {
+ // visible() is not the same thing as being show()n exactly, but
+ // show()/hide() set it appropriately for our purposes.
+ ret = widget->visible();
+ }
+ return ret;
+}
+
bool FltkResource::isEnabled ()
{
- /** \bug Not implemented. */
- return true;
+ return enabled;
}
void FltkResource::setEnabled (bool enabled)
{
- /** \bug Not implemented. */
+ this->enabled = enabled;
+
+ if (enabled)
+ widget->activate ();
+ else
+ widget->deactivate ();
}
// ----------------------------------------------------------------------
@@ -286,11 +283,47 @@ void FltkLabelButtonResource::sizeRequest (core::Requisition *requisition)
}
}
+/*
+ * Get FLTK state and translate to dw
+ *
+ * TODO: find a good home for this and the fltkviewbase.cc original.
+ */
+static core::ButtonState getDwButtonState ()
+{
+ int s1 = ::fltk::event_state ();
+ int s2 = (core::ButtonState)0;
+
+ if (s1 & ::fltk::SHIFT) s2 |= core::SHIFT_MASK;
+ if (s1 & ::fltk::CTRL) s2 |= core::CONTROL_MASK;
+ if (s1 & ::fltk::ALT) s2 |= core::META_MASK;
+ if (s1 & ::fltk::BUTTON1) s2 |= core::BUTTON1_MASK;
+ if (s1 & ::fltk::BUTTON2) s2 |= core::BUTTON2_MASK;
+ if (s1 & ::fltk::BUTTON3) s2 |= core::BUTTON3_MASK;
+
+ return (core::ButtonState)s2;
+}
+
+static void setButtonEvent(dw::core::EventButton *event)
+{
+ event->xCanvas = ::fltk::event_x();
+ event->yCanvas = ::fltk::event_y();
+ event->state = getDwButtonState();
+ event->button = ::fltk::event_button();
+ event->numPressed = ::fltk::event_clicks() + 1;
+}
+
void FltkLabelButtonResource::widgetCallback (::fltk::Widget *widget,
void *data)
{
- if (widget->when () & ::fltk::WHEN_RELEASE)
- ((FltkLabelButtonResource*)data)->emitActivate ();
+ if ((widget->when () & ::fltk::WHEN_RELEASE) &&
+ ((::fltk::event_key() == ::fltk::ReturnKey) ||
+ (::fltk::event_button() == ::fltk::LeftButton ||
+ ::fltk::event_button() == ::fltk::MiddleButton))) {
+ FltkLabelButtonResource *lbr = (FltkLabelButtonResource*) data;
+ dw::core::EventButton event;
+ setButtonEvent(&event);
+ lbr->emitClicked(&event);
+ }
}
const char *FltkLabelButtonResource::getLabel ()
@@ -304,12 +337,7 @@ void FltkLabelButtonResource::setLabel (const char *label)
delete this->label;
this->label = strdup (label);
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- viewAndWidget->widget->label (this->label);
- }
-
+ widget->label (this->label);
queueResize (true);
}
@@ -320,7 +348,7 @@ FltkComplexButtonResource::FltkComplexButtonResource (FltkPlatform *platform,
*widget, bool relief):
FltkSpecificResource <dw::core::ui::ComplexButtonResource> (platform)
{
- viewsAndViews = new container::typed::List <ViewAndView> (true);
+ flatView = topView = NULL;
this->relief = relief;
FltkResource::init (platform);
ComplexButtonResource::init (widget);
@@ -328,7 +356,6 @@ FltkComplexButtonResource::FltkComplexButtonResource (FltkPlatform *platform,
FltkComplexButtonResource::~FltkComplexButtonResource ()
{
- delete viewsAndViews;
}
void FltkComplexButtonResource::widgetCallback (::fltk::Widget *widget,
@@ -336,14 +363,17 @@ void FltkComplexButtonResource::widgetCallback (::fltk::Widget *widget,
{
FltkComplexButtonResource *res = (FltkComplexButtonResource*)data;
- /* would be best not to send click pos. if the image could not be loaded */
- if (::fltk::event() == ::fltk::RELEASE &&
- ::fltk::event_button() == ::fltk::LeftButton) {
+ if (widget->when() == ::fltk::WHEN_RELEASE &&
+ ((::fltk::event_key() == ::fltk::ReturnKey) ||
+ (::fltk::event_button() == ::fltk::LeftButton ||
+ ::fltk::event_button() == ::fltk::MiddleButton))) {
res->click_x = ::fltk::event_x();
res->click_y = ::fltk::event_y();
- res->emitActivate ();
+ dw::core::EventButton event;
+ setButtonEvent(&event);
+ res->emitClicked(&event);
} else {
- ((FltkViewBase*)res->lastFlatView)->handle(::fltk::event());
+ ((FltkViewBase*)res->flatView)->handle(::fltk::event());
}
}
@@ -356,56 +386,30 @@ void FltkComplexButtonResource::attachView (FltkView *view)
{
FltkResource::attachView (view);
- if (view->usesFltkWidgets ()) {
- ViewAndView *viewAndView = new ViewAndView();
- viewAndView->topView = view;
- viewAndView->flatView = lastFlatView;
- viewsAndViews->append (viewAndView);
- }
+ if (view->usesFltkWidgets ())
+ topView = view;
}
void FltkComplexButtonResource::detachView (FltkView *view)
{
FltkResource::detachView (view);
-
- for (Iterator <ViewAndView> it = viewsAndViews->iterator ();
- it.hasNext(); ) {
- ViewAndView *viewAndView = it.getNext ();
- if (viewAndView->topView == view) {
- viewsAndViews->removeRef (viewAndView);
- return;
- }
- }
-
- fprintf (stderr,
- "FltkComplexButtonResourceResource::detachView: View not "
- "found.\n");
}
void FltkComplexButtonResource::sizeAllocate (core::Allocation *allocation)
{
FltkResource::sizeAllocate (allocation);
- for (Iterator <ViewAndView> it = viewsAndViews->iterator ();
- it.hasNext(); ) {
- ViewAndView *viewAndView = it.getNext ();
- ((FltkFlatView*)viewAndView->flatView)->resize (
- reliefXThickness (),
- reliefYThickness (),
- allocation->width - 2 * reliefXThickness (),
- allocation->ascent + allocation->descent - 2 * reliefYThickness ());
-
- ((FltkFlatView*)viewAndView->flatView)->parent ()->init_sizes ();
- }
+ ((FltkFlatView*)flatView)->resize (
+ reliefXThickness (), reliefYThickness (),
+ allocation->width - 2 * reliefXThickness (),
+ allocation->ascent + allocation->descent - 2 * reliefYThickness ());
+
+ ((FltkFlatView*)flatView)->parent ()->init_sizes ();
}
void FltkComplexButtonResource::setLayout (dw::core::Layout *layout)
{
- for (Iterator <ViewAndView> it = viewsAndViews->iterator ();
- it.hasNext(); ) {
- ViewAndView *viewAndView = it.getNext ();
- layout->attachView (viewAndView->flatView);
- }
+ layout->attachView (flatView);
}
int FltkComplexButtonResource::reliefXThickness ()
@@ -430,30 +434,28 @@ int FltkComplexButtonResource::reliefYThickness ()
if (!relief)
button->box(::fltk::FLAT_BOX);
- FltkFlatView *flatView =
- new FltkFlatView (allocation->x + reliefXThickness (),
- allocation->y + reliefYThickness (),
- allocation->width - 2 * reliefXThickness (),
- allocation->ascent + allocation->descent
- - 2 * reliefYThickness ());
- button->add (flatView);
-
- lastFlatView = flatView;
+ flatView = new FltkFlatView (allocation->x + reliefXThickness (),
+ allocation->y + reliefYThickness (),
+ allocation->width - 2 * reliefXThickness (),
+ allocation->ascent + allocation->descent
+ - 2 * reliefYThickness ());
+ button->add ((FltkFlatView *)flatView);
if (layout)
- layout->attachView (lastFlatView);
+ layout->attachView (flatView);
return button;
}
// ----------------------------------------------------------------------
FltkEntryResource::FltkEntryResource (FltkPlatform *platform, int maxLength,
- bool password):
+ bool password, const char *label):
FltkSpecificResource <dw::core::ui::EntryResource> (platform)
{
this->maxLength = maxLength;
this->password = password;
-
+ this->label = label ? strdup(label) : NULL;
+
initText = NULL;
editable = false;
@@ -464,46 +466,51 @@ FltkEntryResource::~FltkEntryResource ()
{
if (initText)
delete initText;
+ if (label)
+ delete label;
}
::fltk::Widget *FltkEntryResource::createNewWidget (core::Allocation
*allocation)
{
::fltk::Input *input =
- password ?
- new ::fltk::SecretInput (allocation->x, allocation->y,
- allocation->width,
- allocation->ascent + allocation->descent) :
new ::fltk::Input (allocation->x, allocation->y, allocation->width,
allocation->ascent + allocation->descent);
+ if (password)
+ input->type(::fltk::Input::SECRET);
input->callback (widgetCallback, this);
input->when (::fltk::WHEN_ENTER_KEY_ALWAYS);
- if (viewsAndWidgets->isEmpty ()) {
- // First widget created, attach the set text.
- if (initText)
- input->value (initText);
- } else
- input->value
- (((::fltk::Input*)viewsAndWidgets->getFirst()->widget)->value ());
+ if (label) {
+ input->label(label);
+ input->set_flag(::fltk::ALIGN_INSIDE_LEFT);
+ }
+ if (initText)
+ input->value (initText);
return input;
}
+void FltkEntryResource::setDisplayed(bool displayed)
+{
+ FltkResource::setDisplayed(displayed);
+ queueResize(true);
+}
+
void FltkEntryResource::sizeRequest (core::Requisition *requisition)
{
- if (style) {
+ if (displayed() && style) {
FltkFont *font = (FltkFont*)style->font;
::fltk::setfont(font->font,font->size);
requisition->width =
- (int)::fltk::getwidth ("M", 1)
+ (int)::fltk::getwidth ("n", 1)
* (maxLength == UNLIMITED_MAX_LENGTH ? 10 : maxLength)
+ 2 * RELIEF_X_THICKNESS;
requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
requisition->descent = font->descent + RELIEF_Y_THICKNESS;
} else {
- requisition->width = 1;
- requisition->ascent = 1;
+ requisition->width = 0;
+ requisition->ascent = 0;
requisition->descent = 0;
}
}
@@ -517,7 +524,7 @@ void FltkEntryResource::widgetCallback (::fltk::Widget *widget,
* The Back or Forward, buttons, or the first click on a rendered
* page. BUG: this must be investigated and reported to FLTK2 team
*/
- printf ("when = %d\n", widget->when ());
+ MSG("when = %d\n", widget->when ());
if ((widget->when () & ::fltk::WHEN_ENTER_KEY_ALWAYS) &&
(::fltk::event_key() == ::fltk::ReturnKey))
((FltkEntryResource*)data)->emitActivate ();
@@ -525,10 +532,7 @@ void FltkEntryResource::widgetCallback (::fltk::Widget *widget,
const char *FltkEntryResource::getText ()
{
- if (viewsAndWidgets->isEmpty ())
- return initText;
- else
- return ((::fltk::Input*)viewsAndWidgets->getFirst()->widget)->value ();
+ return ((::fltk::Input*)widget)->value ();
}
void FltkEntryResource::setText (const char *text)
@@ -537,11 +541,7 @@ void FltkEntryResource::setText (const char *text)
delete initText;
initText = strdup (text);
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- ((::fltk::Input*)viewAndWidget->widget)->value (initText);
- }
+ ((::fltk::Input*)widget)->value (initText);
}
bool FltkEntryResource::isEditable ()
@@ -557,7 +557,7 @@ void FltkEntryResource::setEditable (bool editable)
// ----------------------------------------------------------------------
FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform,
- int cols, int rows):
+ int cols, int rows):
FltkSpecificResource <dw::core::ui::MultiLineTextResource> (platform)
{
buffer = new ::fltk::TextBuffer;
@@ -568,11 +568,11 @@ FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform,
// Check values. Upper bound check is left to the caller.
if (numCols < 1) {
- fprintf (stderr, "WARNING: numCols = %d is set to 1.\n", numCols);
+ MSG_WARN("numCols = %d is set to 1.\n", numCols);
numCols = 1;
}
if (numRows < 1) {
- fprintf (stderr, "WARNING: numRows = %d is set to 1.\n", numRows);
+ MSG_WARN("numRows = %d is set to 1.\n", numRows);
numRows = 1;
}
@@ -582,11 +582,7 @@ FltkMultiLineTextResource::FltkMultiLineTextResource (FltkPlatform *platform,
FltkMultiLineTextResource::~FltkMultiLineTextResource ()
{
/* Free memory avoiding a double-free of text buffers */
- for (Iterator <ViewAndWidget> it = viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- ViewAndWidget *viewAndWidget = it.getNext ();
- ((::fltk::TextEditor *) viewAndWidget->widget)->buffer (0);
- }
+ ((::fltk::TextEditor *) widget)->buffer (0);
delete buffer;
}
@@ -607,13 +603,13 @@ void FltkMultiLineTextResource::sizeRequest (core::Requisition *requisition)
FltkFont *font = (FltkFont*)style->font;
::fltk::setfont(font->font,font->size);
requisition->width =
- (int)::fltk::getwidth ("X", 1) * numCols +
+ (int)::fltk::getwidth ("n", 1) * numCols +
2 * RELIEF_X_THICKNESS;
requisition->ascent =
- font->ascent + RELIEF_Y_THICKNESS;
+ RELIEF_Y_THICKNESS + font->ascent +
+ (font->ascent + font->descent) * (numRows - 1);
requisition->descent =
font->descent +
- (font->ascent + font->descent) * (numRows - 1) +
RELIEF_Y_THICKNESS;
} else {
requisition->width = 1;
@@ -647,7 +643,7 @@ void FltkMultiLineTextResource::setEditable (bool editable)
template <class I>
FltkToggleButtonResource<I>::FltkToggleButtonResource (FltkPlatform *platform,
bool activated):
- FltkSpecificResource <I> (platform)
+ FltkSpecificResource <I> (platform)
{
initActivated = activated;
}
@@ -664,13 +660,7 @@ template <class I>
*allocation)
{
::fltk::Button *button = createNewButton (allocation);
-
- if (this->viewsAndWidgets->isEmpty ())
- button->value (initActivated);
- else
- button->value (((::fltk::Button*)this->viewsAndWidgets
- ->getFirst()->widget)->value ());
-
+ button->value (initActivated);
return button;
}
@@ -697,12 +687,7 @@ void FltkToggleButtonResource<I>::sizeRequest (core::Requisition *requisition)
template <class I>
bool FltkToggleButtonResource<I>::FltkToggleButtonResource::isActivated ()
{
- if (this->viewsAndWidgets->isEmpty ())
- return initActivated;
- else
- return
- ((::fltk::Button*)this->viewsAndWidgets->getFirst()->widget)
- ->value ();
+ return ((::fltk::Button*)this->widget)->value ();
}
@@ -710,13 +695,7 @@ template <class I>
void FltkToggleButtonResource<I>::setActivated (bool activated)
{
initActivated = activated;
-
- for (Iterator <FltkResource::ViewAndWidget> it =
- this->viewsAndWidgets->iterator ();
- it.hasNext(); ) {
- FltkResource::ViewAndWidget *viewAndWidget = it.getNext ();
- ((::fltk::Button*)viewAndWidget->widget)->value (initActivated);
- }
+ ((::fltk::Button*)this->widget)->value (initActivated);
}
// ----------------------------------------------------------------------
@@ -736,7 +715,7 @@ FltkCheckButtonResource::~FltkCheckButtonResource ()
::fltk::Button *FltkCheckButtonResource::createNewButton (core::Allocation
- *allocation)
+ *allocation)
{
::fltk::CheckButton *cb =
new ::fltk::CheckButton (allocation->x, allocation->y, allocation->width,
@@ -767,7 +746,7 @@ void FltkRadioButtonResource::Group::FltkGroupIterator::unref ()
FltkRadioButtonResource::Group::Group (FltkRadioButtonResource
*radioButtonResource)
{
- list = new container::typed::List <FltkRadioButtonResource> (false);
+ list = new lout::container::typed::List <FltkRadioButtonResource> (false);
connect (radioButtonResource);
}
@@ -775,7 +754,7 @@ FltkRadioButtonResource::Group::~Group ()
{
delete list;
}
-
+
void FltkRadioButtonResource::Group::connect (FltkRadioButtonResource
*radioButtonResource)
{
@@ -836,7 +815,7 @@ void FltkRadioButtonResource::buttonClicked ()
}
::fltk::Button *FltkRadioButtonResource::createNewButton (core::Allocation
- *allocation)
+ *allocation)
{
/*
* Groups of fltk::RadioButton must be added to one fltk::Group, which is
@@ -1025,7 +1004,7 @@ template <class I> void FltkSelectionResource<I>::addItem (const char *str,
}
}
}
-
+
template <class I> void FltkSelectionResource<I>::pushGroup (const char *name,
bool enabled)
{
@@ -1038,7 +1017,7 @@ template <class I> void FltkSelectionResource<I>::pushGroup (const char *name,
::fltk::ItemGroup *group = item->createNewGroupWidget ();
widgetStack->stack->getTop()->getTypedValue()->add (group);
widgetStack->stack->push (new TypedPointer < ::fltk::Menu> (group));
- if(!enabled)
+ if (!enabled)
group->deactivate ();
}
}
@@ -1123,7 +1102,7 @@ void FltkOptionMenuResource::sizeRequest (core::Requisition *requisition)
requisition->width = 1;
requisition->ascent = 1;
requisition->descent = 0;
- }
+ }
}
void FltkOptionMenuResource::addItem (const char *str,
@@ -1146,10 +1125,12 @@ bool FltkOptionMenuResource::isSelected (int index)
FltkListResource::FltkListResource (FltkPlatform *platform,
core::ui::ListResource::SelectionMode
- selectionMode):
+ selectionMode, int rowCount):
FltkSelectionResource <dw::core::ui::ListResource> (platform),
itemsSelected(8)
{
+ mode = selectionMode;
+ showRows = rowCount;
init (platform);
}
@@ -1161,9 +1142,10 @@ FltkListResource::~FltkListResource ()
::fltk::Menu *FltkListResource::createNewMenu (core::Allocation *allocation)
{
::fltk::Menu *menu =
- new ::fltk::MultiBrowser (allocation->x, allocation->y,
- allocation->width,
- allocation->ascent + allocation->descent);
+ new ::fltk::Browser (allocation->x, allocation->y, allocation->width,
+ allocation->ascent + allocation->descent);
+ if (mode == SELECTION_MULTIPLE)
+ menu->type(::fltk::Browser::MULTI);
menu->set_flag (::fltk::RAW_LABEL);
menu->callback(widgetCallback,this);
menu->when(::fltk::WHEN_CHANGED);
@@ -1173,10 +1155,25 @@ FltkListResource::~FltkListResource ()
void FltkListResource::widgetCallback (::fltk::Widget *widget, void *data)
{
::fltk::Widget *fltkItem = ((::fltk::Menu *) widget)->item ();
- int index = (long) (fltkItem->user_data ());
+ int index = -1;
+ if (fltkItem)
+ index = (long) (fltkItem->user_data ());
if (index > -1) {
- bool selected = fltkItem->selected ();
- ((FltkListResource *) data)->itemsSelected.set (index, selected);
+ /* A MultiBrowser will trigger a callback for each item that is
+ * selected and each item that is deselected, but a "plain"
+ * Browser will only trigger the callback for the newly selected item
+ * (for which selected() is false, incidentally).
+ */
+ FltkListResource *res = (FltkListResource *) data;
+ if (res->mode == SELECTION_MULTIPLE) {
+ bool selected = fltkItem->selected ();
+ res->itemsSelected.set (index, selected);
+ } else {
+ int size = res->itemsSelected.size();
+ for (int i = 0; i < size; i++)
+ res->itemsSelected.set (i, false);
+ res->itemsSelected.set (index, true);
+ }
}
}
@@ -1195,18 +1192,24 @@ void FltkListResource::sizeRequest (core::Requisition *requisition)
if (style) {
FltkFont *font = (FltkFont*)style->font;
::fltk::setfont(font->font,font->size);
- int maxStringWidth = getMaxStringWidth ();
- requisition->ascent = font->ascent + RELIEF_Y_THICKNESS;
- requisition->descent =
- (getNumberOfItems () - 1) * (font->ascent) + font->descent;
- requisition->width = maxStringWidth
- + (font->ascent + font->descent) * 4 / 5
- + 2 * RELIEF_X_THICKNESS;
+ int rows = getNumberOfItems();
+ if (showRows < rows) {
+ rows = showRows;
+ }
+ /*
+ * The widget sometimes shows scrollbars when they are not required.
+ * The following values try to keep any scrollbars from obscuring
+ * options, at the cost of showing too much whitespace at times.
+ */
+ requisition->width = getMaxStringWidth() + 24;
+ requisition->ascent = font->ascent + 2 +
+ (rows - 1) * (font->ascent + font->descent + 1);
+ requisition->descent = font->descent + 3;
} else {
requisition->width = 1;
requisition->ascent = 1;
requisition->descent = 0;
- }
+ }
}
bool FltkListResource::isSelected (int index)
diff --git a/dw/fltkui.hh b/dw/fltkui.hh
index 4de99d27..245d5aad 100644
--- a/dw/fltkui.hh
+++ b/dw/fltkui.hh
@@ -14,8 +14,6 @@
namespace dw {
namespace fltk {
-using namespace lout;
-
/**
* \brief FLTK implementation of dw::core::ui.
*
@@ -27,7 +25,7 @@ using namespace lout;
* edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
* labelfontsize=10, color="#404040", labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
- *
+ *
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
@@ -67,10 +65,10 @@ using namespace lout;
* dw::core::ui::Resource is ambiguous for
* dw::fltk::ui::FltkLabelButtonResource.
*
- * To solve this, we have to remove the depencency between
+ * To solve this, we have to remove the dependency between
* dw::fltk::ui::FltkResource and dw::core::ui::Resource, instead, the part
* of dw::core::ui::Resource, which is implemented in
- * dw::fltk::ui::FltkResource, must be explicitely delegated from
+ * dw::fltk::ui::FltkResource, must be explicitly delegated from
* dw::fltk::ui::FltkLabelButtonResourceto dw::fltk::ui::FltkResource:
*
* \dot
@@ -79,7 +77,7 @@ using namespace lout;
* edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
* labelfontsize=10, color="#404040", labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
- *
+ *
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
@@ -120,7 +118,7 @@ using namespace lout;
* edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
* labelfontsize=10, color="#404040", labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
- *
+ *
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
@@ -174,17 +172,14 @@ namespace ui {
/**
* ...
*/
-class FltkResource: public object::Object
+class FltkResource: public lout::object::Object
{
-protected:
- class ViewAndWidget: public object::Object
- {
- public:
- FltkView *view;
- ::fltk::Widget *widget;
- };
+private:
+ bool enabled;
- container::typed::List <ViewAndWidget> *viewsAndWidgets;
+protected:
+ FltkView *view;
+ ::fltk::Widget *widget;
core::Allocation allocation;
FltkPlatform *platform;
@@ -195,12 +190,14 @@ protected:
virtual ::fltk::Widget *createNewWidget (core::Allocation *allocation) = 0;
void setWidgetStyle (::fltk::Widget *widget, core::style::Style *style);
+ void setDisplayed (bool displayed);
+ bool displayed();
public:
~FltkResource ();
virtual void attachView (FltkView *view);
virtual void detachView (FltkView *view);
-
+
void sizeAllocate (core::Allocation *allocation);
void draw (core::View *view, core::Rectangle *area);
@@ -257,15 +254,7 @@ private:
static void widgetCallback (::fltk::Widget *widget, void *data);
protected:
- class ViewAndView: public object::Object
- {
- public:
- FltkView *topView, *flatView;
- };
-
- FltkView *lastFlatView;
-
- container::typed::List <ViewAndView> *viewsAndViews;
+ FltkView *topView, *flatView;
void attachView (FltkView *view);
void detachView (FltkView *view);
@@ -274,7 +263,7 @@ protected:
dw::core::Platform *createPlatform ();
void setLayout (dw::core::Layout *layout);
-
+
int reliefXThickness ();
int reliefYThickness ();
@@ -298,15 +287,18 @@ private:
int maxLength;
bool password;
const char *initText;
+ char *label;
bool editable;
static void widgetCallback (::fltk::Widget *widget, void *data);
+ void setDisplayed (bool displayed);
protected:
::fltk::Widget *createNewWidget (core::Allocation *allocation);
public:
- FltkEntryResource (FltkPlatform *platform, int maxLength, bool password);
+ FltkEntryResource (FltkPlatform *platform, int maxLength, bool password,
+ const char *label);
~FltkEntryResource ();
void sizeRequest (core::Requisition *requisition);
@@ -351,7 +343,7 @@ private:
protected:
virtual ::fltk::Button *createNewButton (core::Allocation *allocation) = 0;
::fltk::Widget *createNewWidget (core::Allocation *allocation);
-
+
public:
FltkToggleButtonResource (FltkPlatform *platform,
bool activated);
@@ -388,10 +380,10 @@ private:
public dw::core::ui::RadioButtonResource::GroupIterator
{
private:
- container::typed::Iterator <FltkRadioButtonResource> it;
+ lout::container::typed::Iterator <FltkRadioButtonResource> it;
public:
- inline FltkGroupIterator (container::typed::List
+ inline FltkGroupIterator (lout::container::typed::List
<FltkRadioButtonResource>
*list)
{ it = list->iterator (); }
@@ -401,15 +393,15 @@ private:
void unref ();
};
- container::typed::List <FltkRadioButtonResource> *list;
+ lout::container::typed::List <FltkRadioButtonResource> *list;
protected:
~Group ();
public:
Group (FltkRadioButtonResource *radioButtonResource);
-
- inline container::typed::Iterator <FltkRadioButtonResource> iterator ()
+
+ inline lout::container::typed::Iterator <FltkRadioButtonResource> iterator ()
{
return list->iterator ();
}
@@ -446,7 +438,7 @@ template <class I> class FltkSelectionResource:
public FltkSpecificResource <I>
{
protected:
- class Item: public object::Object
+ class Item: public lout::object::Object
{
public:
enum Type { ITEM, START, END } type;
@@ -462,19 +454,19 @@ protected:
::fltk::ItemGroup *createNewGroupWidget ();
};
- class WidgetStack: public object::Object
+ class WidgetStack: public lout::object::Object
{
public:
::fltk::Menu *widget;
- container::typed::Stack <object::TypedPointer < ::fltk::Menu> > *stack;
+ lout::container::typed::Stack <lout::object::TypedPointer < ::fltk::Menu> > *stack;
WidgetStack (::fltk::Menu *widget);
~WidgetStack ();
};
- container::typed::List <WidgetStack> *widgetStacks;
- container::typed::List <Item> *allItems;
- container::typed::Vector <Item> *items;
+ lout::container::typed::List <WidgetStack> *widgetStacks;
+ lout::container::typed::List <Item> *allItems;
+ lout::container::typed::Vector <Item> *items;
Item *createNewItem (typename Item::Type type,
const char *name = NULL,
@@ -491,10 +483,10 @@ public:
FltkSelectionResource (FltkPlatform *platform);
~FltkSelectionResource ();
- dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd);
+ dw::core::Iterator *iterator (dw::core::Content::Type mask, bool atEnd);
void addItem (const char *str, bool enabled, bool selected);
-
+
void pushGroup (const char *name, bool enabled);
void popGroup ();
@@ -532,11 +524,13 @@ protected:
private:
static void widgetCallback (::fltk::Widget *widget, void *data);
- misc::SimpleVector <bool> itemsSelected;
-
+ lout::misc::SimpleVector <bool> itemsSelected;
+ int showRows;
+ ListResource::SelectionMode mode;
public:
FltkListResource (FltkPlatform *platform,
- core::ui::ListResource::SelectionMode selectionMode);
+ core::ui::ListResource::SelectionMode selectionMode,
+ int rows);
~FltkListResource ();
void addItem (const char *str, bool enabled, bool selected);
diff --git a/dw/fltkviewbase.cc b/dw/fltkviewbase.cc
index b35b07df..7d519f05 100644
--- a/dw/fltkviewbase.cc
+++ b/dw/fltkviewbase.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -30,6 +29,7 @@
#include <fltk/run.h>
#include <stdio.h>
+#include "../lout/msg.h"
using namespace fltk;
using namespace lout::object;
@@ -47,9 +47,8 @@ FltkViewBase::FltkViewBase (int x, int y, int w, int h, const char *label):
canvasWidth = 1;
canvasHeight = 1;
bgColor = WHITE;
- lastDraw = time(0);
- drawDelay = 2; /* in seconds */
mouse_x = mouse_y = 0;
+ exposeArea = NULL;
if (backBuffer == NULL) {
backBuffer = new Image ();
}
@@ -74,10 +73,10 @@ void FltkViewBase::draw ()
int d = damage ();
if ((d & DAMAGE_VALUE) && !(d & DAMAGE_EXPOSE)) {
- container::typed::Iterator <core::Rectangle> it;
+ lout::container::typed::Iterator <core::Rectangle> it;
for (it = drawRegion.rectangles (); it.hasNext (); ) {
- drawRectangle (it.getNext (), true);
+ draw (it.getNext (), DRAW_BUFFERED);
}
drawRegion.clear ();
@@ -96,16 +95,18 @@ void FltkViewBase::draw ()
w (),
h ());
- drawRectangle (&rect, false);
-
- if (! (d & DAMAGE_SCROLL)) {
+ if (d == DAMAGE_SCROLL) {
+ // a clipping rectangle has already been set by fltk::scrollrect ()
+ draw (&rect, DRAW_PLAIN);
+ } else {
+ draw (&rect, DRAW_CLIPPED);
drawRegion.clear ();
}
}
}
-void FltkViewBase::drawRectangle (const core::Rectangle *rect,
- bool doubleBuffer)
+void FltkViewBase::draw (const core::Rectangle *rect,
+ DrawType type)
{
int offsetX = 0, offsetY = 0;
@@ -116,7 +117,7 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect,
translateCanvasXToViewX (rect->x) + offsetX,
translateCanvasYToViewY (rect->y) + offsetY,
rect->width, rect->height);
-
+
::fltk::intersect_with_clip (viewRect);
viewRect.x (viewRect.x () - offsetX);
@@ -129,14 +130,16 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect,
viewRect.w (),
viewRect.h ());
- if (doubleBuffer && backBuffer && !backBufferInUse) {
+ exposeArea = &viewRect;
+
+ if (type == DRAW_BUFFERED && backBuffer && !backBufferInUse) {
backBufferInUse = true;
{
GSave gsave;
backBuffer->setsize (viewRect.w (), viewRect.h ());
backBuffer->make_current ();
- translate (-viewRect.x (), -viewRect.y ());
+ translate (-viewRect.x (), -viewRect.y ());
setcolor (bgColor);
fillrect (viewRect);
@@ -147,19 +150,21 @@ void FltkViewBase::drawRectangle (const core::Rectangle *rect,
viewRect);
backBufferInUse = false;
- } else if (doubleBuffer) {
+ } else if (type == DRAW_BUFFERED || type == DRAW_CLIPPED) {
+ // if type == DRAW_BUFFERED but we do not have backBuffer available
+ // we fall back to clipped drawing
push_clip (viewRect);
setcolor (bgColor);
fillrect (viewRect);
theLayout->expose (this, &r);
pop_clip ();
} else {
- // if doubleBuffer is false we assume that a clipping
- // rectangle has been set already
setcolor (bgColor);
fillrect (viewRect);
theLayout->expose (this, &r);
}
+
+ exposeArea = NULL;
}
}
@@ -178,13 +183,13 @@ core::ButtonState getDwButtonState ()
{
int s1 = event_state ();
int s2 = (core::ButtonState)0;
-
- if(s1 & SHIFT) s2 |= core::SHIFT_MASK;
- if(s1 & CTRL) s2 |= core::CONTROL_MASK;
- if(s1 & ALT) s2 |= core::META_MASK;
- if(s1 & BUTTON1) s2 |= core::BUTTON1_MASK;
- if(s1 & BUTTON2) s2 |= core::BUTTON2_MASK;
- if(s1 & BUTTON3) s2 |= core::BUTTON3_MASK;
+
+ if (s1 & SHIFT) s2 |= core::SHIFT_MASK;
+ if (s1 & CTRL) s2 |= core::CONTROL_MASK;
+ if (s1 & ALT) s2 |= core::META_MASK;
+ if (s1 & BUTTON1) s2 |= core::BUTTON1_MASK;
+ if (s1 & BUTTON2) s2 |= core::BUTTON2_MASK;
+ if (s1 & BUTTON3) s2 |= core::BUTTON3_MASK;
return (core::ButtonState)s2;
}
@@ -194,7 +199,7 @@ int FltkViewBase::handle (int event)
bool processed;
/**
- * \todo Consider, whether this from the FLTK documentation has any
+ * \todo Consider, whether this from the FLTK documentation has any
* impacts: "To receive fltk::RELEASE events you must return non-zero
* when passed a fltk::PUSH event. "
*/
@@ -205,7 +210,11 @@ int FltkViewBase::handle (int event)
translateViewXToCanvasX (event_x ()),
translateViewYToCanvasY (event_y ()),
getDwButtonState (), event_button ());
- //printf ("PUSH => %s\n", processed ? "true" : "false");
+ _MSG("PUSH => %s\n", processed ? "true" : "false");
+ if (processed) {
+ /* pressed dw content; give focus to the view */
+ ::fltk::focus(this);
+ }
return processed ? true : Group::handle (event);
case RELEASE:
@@ -214,7 +223,7 @@ int FltkViewBase::handle (int event)
translateViewXToCanvasX (event_x ()),
translateViewYToCanvasY (event_y ()),
getDwButtonState (), event_button ());
- //printf ("RELEASE => %s\n", processed ? "true" : "false");
+ _MSG("RELEASE => %s\n", processed ? "true" : "false");
return processed ? true : Group::handle (event);
case MOVE:
@@ -225,7 +234,7 @@ int FltkViewBase::handle (int event)
translateViewXToCanvasX (mouse_x),
translateViewYToCanvasY (mouse_y),
getDwButtonState ());
- //printf ("MOVE => %s\n", processed ? "true" : "false");
+ _MSG("MOVE => %s\n", processed ? "true" : "false");
return processed ? true : Group::handle (event);
case DRAG:
@@ -234,7 +243,7 @@ int FltkViewBase::handle (int event)
translateViewXToCanvasX (event_x ()),
translateViewYToCanvasY (event_y ()),
getDwButtonState ());
- //printf ("DRAG => %s\n", processed ? "true" : "false");
+ _MSG("DRAG => %s\n", processed ? "true" : "false");
return processed ? true : Group::handle (event);
case ENTER:
@@ -246,7 +255,7 @@ int FltkViewBase::handle (int event)
case LEAVE:
theLayout->leaveNotify (this, getDwButtonState ());
return Group::handle (event);
-
+
default:
return Group::handle (event);
}
@@ -269,7 +278,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor)
{
static Cursor *mapDwToFltk[] = {
CURSOR_CROSS,
- CURSOR_ARROW,
+ CURSOR_DEFAULT,
CURSOR_HAND,
CURSOR_MOVE,
CURSOR_WE,
@@ -288,7 +297,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor)
/*
static char *cursorName[] = {
"CURSOR_CROSS",
- "CURSOR_ARROW",
+ "CURSOR_DEFAULT",
"CURSOR_HAND",
"CURSOR_MOVE",
"CURSOR_WE",
@@ -304,7 +313,7 @@ void FltkViewBase::setCursor (core::style::Cursor cursor)
"CURSOR_HELP"
};
- printf ("Cursor changes to '%s'.\n", cursorName[cursor]);
+ MSG("Cursor changes to '%s'.\n", cursorName[cursor]);
*/
/** \bug Does not work */
@@ -333,33 +342,13 @@ void FltkViewBase::queueDraw (core::Rectangle *area)
redraw (DAMAGE_VALUE);
}
-static void drawTotalTimeout (void *data)
-{
- FltkViewBase *view = (FltkViewBase*) data;
- if (time(0) >= view->lastDraw + view->drawDelay) {
- view->drawTotal ();
- } else {
- ::fltk::add_timeout (0.2f, drawTotalTimeout, data);
- }
-}
-
-void FltkViewBase::drawTotal ()
-{
- //static int calls = 0;
- //printf(" FltkViewBase::drawTotal calls = %d\n", ++calls);
- redraw (DAMAGE_EXPOSE);
- lastDraw = time (0);
- cancelQueueDraw ();
-}
-
void FltkViewBase::queueDrawTotal ()
{
- drawTotal ();
+ redraw (DAMAGE_EXPOSE);
}
void FltkViewBase::cancelQueueDraw ()
{
- ::fltk::remove_timeout (drawTotalTimeout, this);
}
void FltkViewBase::drawPoint (core::style::Color *color,
@@ -383,10 +372,26 @@ void FltkViewBase::drawRectangle (core::style::Color *color,
int x, int y, int width, int height)
{
setcolor(((FltkColor*)color)->colors[shading]);
+ if (width < 0) {
+ x += width;
+ width = -width;
+ }
+ if (height < 0) {
+ y += height;
+ height = -height;
+ }
+
int x1 = translateCanvasXToViewX (x);
int y1 = translateCanvasYToViewY (y);
int x2 = translateCanvasXToViewX (x + width);
int y2 = translateCanvasYToViewY (y + height);
+
+ // We only support rectangles with line width 1px, so we clip with
+ // a rectangle 1px wider and higher than what we actually expose.
+ // This is only really necessary for non-filled rectangles.
+ clipPoint (&x1, &y1, 1);
+ clipPoint (&x2, &y2, 1);
+
::fltk::Rectangle rect (x1, y1, x2 - x1, y2 - y1);
if (filled)
fillrect (rect);
@@ -396,13 +401,13 @@ void FltkViewBase::drawRectangle (core::style::Color *color,
void FltkViewBase::drawArc (core::style::Color *color,
core::style::Color::Shading shading, bool filled,
- int x, int y, int width, int height,
+ int centerX, int centerY, int width, int height,
int angle1, int angle2)
{
setcolor(((FltkColor*)color)->colors[shading]);
- int x1 = translateCanvasXToViewX (x);
- int y1 = translateCanvasYToViewY (y);
- ::fltk::Rectangle rect (x1, y1, width, height);
+ int x = translateCanvasXToViewX (centerX) - width / 2;
+ int y = translateCanvasYToViewY (centerY) - height / 2;
+ ::fltk::Rectangle rect (x, y, width, height);
addchord(rect, angle1, angle2);
closepath();
if (filled)
@@ -427,7 +432,7 @@ void FltkViewBase::drawPolygon (core::style::Color *color,
fillpath();
else
strokepath();
- }
+ }
}
core::View *FltkViewBase::getClippingView (int x, int y, int width, int height)
@@ -456,7 +461,7 @@ FltkWidgetView::~FltkWidgetView ()
void FltkWidgetView::layout () {
/**
- * pass layout to child widgets. This is needed for complex fltk
+ * pass layout to child widgets. This is needed for complex fltk
* widgets as TextEditor.
* We can't use Group::layout() as that would rearrange the widgets.
*/
@@ -477,8 +482,23 @@ void FltkWidgetView::drawText (core::style::Font *font,
FltkFont *ff = (FltkFont*)font;
setfont(ff->font, ff->size);
setcolor(((FltkColor*)color)->colors[shading]);
- drawtext(text, len,
- translateCanvasXToViewX (x), translateCanvasYToViewY (y));
+
+ if (!font->letterSpacing) {
+ drawtext(text, len,
+ translateCanvasXToViewX (x), translateCanvasYToViewY (y));
+ } else {
+ /* Nonzero letter spacing adjustment, draw each glyph individually */
+ int viewX = translateCanvasXToViewX (x),
+ viewY = translateCanvasYToViewY (y);
+ int curr = 0, next = 0;
+
+ while (next < len) {
+ next = theLayout->nextGlyph(text, curr);
+ drawtext(text + curr, next - curr, viewX, viewY);
+ viewX += font->letterSpacing + (int)getwidth(text + curr,next - curr);
+ curr = next;
+ }
+ }
}
void FltkWidgetView::drawImage (core::Imgbuf *imgbuf, int xRoot, int yRoot,
diff --git a/dw/fltkviewbase.hh b/dw/fltkviewbase.hh
index fcc2b43d..5e1981f8 100644
--- a/dw/fltkviewbase.hh
+++ b/dw/fltkviewbase.hh
@@ -16,17 +16,28 @@ namespace fltk {
class FltkViewBase: public FltkView, public ::fltk::Group
{
private:
+ typedef enum { DRAW_PLAIN, DRAW_CLIPPED, DRAW_BUFFERED } DrawType;
+
int bgColor;
core::Region drawRegion;
+ ::fltk::Rectangle *exposeArea;
static ::fltk::Image *backBuffer;
static bool backBufferInUse;
- void drawRectangle (const core::Rectangle *rect, bool doubleBuffer);
+ void draw (const core::Rectangle *rect, DrawType type);
void drawChildWidgets ();
-
-public:
- time_t lastDraw;
- time_t drawDelay;
+ inline void clipPoint (int *x, int *y, int border) {
+ if (exposeArea) {
+ if (*x < exposeArea->x () - border)
+ *x = exposeArea->x () - border;
+ if (*x > exposeArea->r () + border)
+ *x = exposeArea->r () + border;
+ if (*y < exposeArea->y () - border)
+ *y = exposeArea->y () - border;
+ if (*y > exposeArea->b () + border)
+ *y = exposeArea->b () + border;
+ }
+ }
protected:
core::Layout *theLayout;
@@ -54,7 +65,6 @@ public:
void finishDrawing (core::Rectangle *area);
void queueDraw (core::Rectangle *area);
void queueDrawTotal ();
- void drawTotal ();
void cancelQueueDraw ();
void drawPoint (core::style::Color *color,
core::style::Color::Shading shading,
@@ -67,7 +77,7 @@ public:
int x, int y, int width, int height);
void drawArc (core::style::Color *color,
core::style::Color::Shading shading, bool filled,
- int x, int y, int width, int height,
+ int centerX, int centerY, int width, int height,
int angle1, int angle2);
void drawPolygon (core::style::Color *color,
core::style::Color::Shading shading,
@@ -84,7 +94,7 @@ class FltkWidgetView: public FltkViewBase
public:
FltkWidgetView (int x, int y, int w, int h, const char *label = 0);
~FltkWidgetView ();
-
+
void layout();
void drawText (core::style::Font *font,
diff --git a/dw/fltkviewport.cc b/dw/fltkviewport.cc
index c999745a..a8555c60 100644
--- a/dw/fltkviewport.cc
+++ b/dw/fltkviewport.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -27,8 +26,10 @@
#include <fltk/events.h>
#include <stdio.h>
+#include "../lout/msg.h"
using namespace fltk;
+using namespace lout;
using namespace lout::object;
using namespace lout::container::typed;
@@ -104,8 +105,8 @@ void FltkViewport::adjustScrollbarsAndGadgetsAllocation ()
vscrollbar->w (SCROLLBAR_THICKNESS);
int x = w () - SCROLLBAR_THICKNESS, y = h () - SCROLLBAR_THICKNESS;
- for(Iterator <TypedPointer < ::fltk::Widget> > it = gadgets->iterator ();
- it.hasNext (); ) {
+ for (Iterator <TypedPointer < ::fltk::Widget> > it = gadgets->iterator ();
+ it.hasNext (); ) {
::fltk::Widget *widget = it.getNext()->getTypedValue ();
widget->x (0);
widget->y (0);
@@ -156,7 +157,7 @@ void FltkViewport::layout ()
{
theLayout->viewportSizeChanged (this, w(), h());
adjustScrollbarsAndGadgetsAllocation ();
-
+
FltkWidgetView::layout ();
}
@@ -167,10 +168,10 @@ void FltkViewport::draw_area (void *data, const Rectangle& cr )
vp->FltkWidgetView::draw ();
- for(Iterator <TypedPointer < ::fltk::Widget> > it = vp->gadgets->iterator ();
- it.hasNext (); ) {
- ::fltk::Widget *widget = it.getNext()->getTypedValue ();
- vp->draw_child (*widget);
+ for (Iterator <TypedPointer < ::fltk::Widget> > it = vp->gadgets->iterator();
+ it.hasNext (); ) {
+ ::fltk::Widget *widget = it.getNext()->getTypedValue ();
+ vp->draw_child (*widget);
}
pop_clip();
@@ -205,13 +206,13 @@ void FltkViewport::draw ()
}
}
- scrollDX = 0;
- scrollDY = 0;
+ scrollDX = 0;
+ scrollDY = 0;
}
int FltkViewport::handle (int event)
{
- //printf("FltkViewport::handle %d\n", event);
+ _MSG("FltkViewport::handle %d\n", event);
if (hscrollbar->Rectangle::contains (event_x (), event_y ()) &&
!(event_state() & (SHIFT | CTRL | ALT)) &&
@@ -225,13 +226,30 @@ int FltkViewport::handle (int event)
}
switch(event) {
+ case ::fltk::KEY:
+ /* Tell fltk we want to receive KEY events as SHORTCUT.
+ * As we don't know the exact keybindings set by the user, we ask
+ * for all of them (except TabKey to keep form navigation). */
+ if (::fltk::event_key() != TabKey)
+ return 0;
+ break;
+
case ::fltk::FOCUS:
/** \bug Draw focus box. */
- return 1;
+
+ /* If the user clicks with the left button we take focus
+ * and thereby unfocus any form widgets.
+ * Otherwise we let fltk do the focus handling.
+ */
+ if (::fltk::event_button() == ::fltk::LeftButton || focus_index() < 0) {
+ focus_index(-1);
+ return 1;
+ }
+ break;
case ::fltk::UNFOCUS:
/** \bug Undraw focus box. */
- return 1;
+ break;
case ::fltk::PUSH:
take_focus();
@@ -261,12 +279,12 @@ int FltkViewport::handle (int event)
case ::fltk:: MOUSEWHEEL:
return (event_dx() ? hscrollbar : vscrollbar)->handle(event);
break;
-
+
case ::fltk::RELEASE:
if (::fltk::event_button() == ::fltk::MiddleButton) {
dragScrolling = 0;
setCursor (core::style::CURSOR_DEFAULT);
- }
+ }
break;
case ::fltk::ENTER:
@@ -279,60 +297,6 @@ int FltkViewport::handle (int event)
case ::fltk::LEAVE:
mouse_x = mouse_y = -1;
break;
-
- case ::fltk::KEY:
- /* tell fltk we want to receive these KEY events as SHORTCUT */
- switch (::fltk::event_key()) {
- case PageUpKey:
- case PageDownKey:
- case SpaceKey:
- case DownKey:
- case UpKey:
- case RightKey:
- case LeftKey:
- case HomeKey:
- case EndKey:
- return 0;
- }
- break;
-
- case ::fltk::SHORTCUT:
- switch (::fltk::event_key()) {
- case PageUpKey:
- case 'b':
- case 'B':
- scroll (0, -vscrollbar->pagesize ());
- return 1;
-
- case PageDownKey:
- case SpaceKey:
- scroll (0, vscrollbar->pagesize ());
- return 1;
-
- case DownKey:
- scroll (0, (int) vscrollbar->linesize ());
- return 1;
-
- case UpKey:
- scroll (0, (int) -vscrollbar->linesize ());
- return 1;
-
- case RightKey:
- scroll ((int) hscrollbar->linesize (), 0);
- return 1;
-
- case LeftKey:
- scroll ((int) -hscrollbar->linesize (), 0);
- return 1;
-
- case HomeKey:
- scrollTo (scrollX, 0);
- return 1;
-
- case EndKey:
- scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */
- return 1;
- }
}
return FltkWidgetView::handle (event);
@@ -361,7 +325,7 @@ void FltkViewport::positionChanged ()
/*
* For scrollbars, this currently sets the same step to both vertical and
* horizontal. It may me differentiated if necessary.
- */
+ */
void FltkViewport::setScrollStep(int step)
{
vscrollbar->linesize(step);
@@ -419,6 +383,27 @@ void FltkViewport::scroll (int dx, int dy)
scrollTo (scrollX + dx, scrollY + dy);
}
+void FltkViewport::scroll (core::ScrollCommand cmd)
+{
+ if (cmd == core::SCREEN_UP_CMD) {
+ scroll (0, -vscrollbar->pagesize ());
+ } else if (cmd == core::SCREEN_DOWN_CMD) {
+ scroll (0, vscrollbar->pagesize ());
+ } else if (cmd == core::LINE_UP_CMD) {
+ scroll (0, (int) -vscrollbar->linesize ());
+ } else if (cmd == core::LINE_DOWN_CMD) {
+ scroll (0, (int) vscrollbar->linesize ());
+ } else if (cmd == core::LEFT_CMD) {
+ scroll ((int) -hscrollbar->linesize (), 0);
+ } else if (cmd == core::RIGHT_CMD) {
+ scroll ((int) hscrollbar->linesize (), 0);
+ } else if (cmd == core::TOP_CMD) {
+ scrollTo (scrollX, 0);
+ } else if (cmd == core::BOTTOM_CMD) {
+ scrollTo (scrollX, canvasHeight); /* gets adjusted in scrollTo () */
+ }
+}
+
void FltkViewport::setViewportSize (int width, int height,
int hScrollbarThickness,
int vScrollbarThickness)
@@ -442,8 +427,8 @@ void FltkViewport::updateCanvasWidgets (int dx, int dy)
// scroll all child widgets except scroll bars
for (int i = children () - 1; i > 0; i--) {
::fltk::Widget *widget = child (i);
-
- if (widget == hscrollbar || widget == vscrollbar)
+
+ if (widget == hscrollbar || widget == vscrollbar)
continue;
widget->x (widget->x () - dx);
diff --git a/dw/fltkviewport.hh b/dw/fltkviewport.hh
index 6af377d4..1e7f54f6 100644
--- a/dw/fltkviewport.hh
+++ b/dw/fltkviewport.hh
@@ -26,7 +26,8 @@ private:
::fltk::Scrollbar *vscrollbar, *hscrollbar;
GadgetOrientation gadgetOrientation[4];
- container::typed::List <object::TypedPointer < ::fltk::Widget> > *gadgets;
+ lout::container::typed::List <lout::object::TypedPointer < ::fltk::Widget> >
+ *gadgets;
void adjustScrollbarsAndGadgetsAllocation ();
void adjustScrollbarValues ();
@@ -49,7 +50,7 @@ protected:
public:
FltkViewport (int x, int y, int w, int h, const char *label = 0);
~FltkViewport ();
-
+
void layout();
void draw ();
int handle (int event);
@@ -60,6 +61,7 @@ public:
int getHScrollbarThickness ();
int getVScrollbarThickness ();
void scroll(int dx, int dy);
+ void scroll(dw::core::ScrollCommand cmd);
void scrollTo (int x, int y);
void setViewportSize (int width, int height,
int hScrollbarThickness, int vScrollbarThickness);
diff --git a/dw/image.cc b/dw/image.cc
index ce54f561..cab40ed5 100644
--- a/dw/image.cc
+++ b/dw/image.cc
@@ -14,13 +14,13 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "image.hh"
+#include "../lout/msg.h"
#include "../lout/misc.hh"
namespace dw {
@@ -38,6 +38,18 @@ ImageMapsList::ImageMap::~ImageMap ()
delete shapesAndLinks;
}
+void ImageMapsList::ImageMap::draw (core::View *view,core::style::Style *style,
+ int x, int y)
+{
+ container::typed::Iterator <ShapeAndLink> it;
+
+ for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
+ ShapeAndLink *shapeAndLink = it.getNext ();
+
+ shapeAndLink->shape->draw(view, style, x, y);
+ }
+}
+
void ImageMapsList::ImageMap::add (core::Shape *shape, int link) {
ShapeAndLink *shapeAndLink = new ShapeAndLink ();
shapeAndLink->shape = shape;
@@ -105,6 +117,15 @@ void ImageMapsList::setCurrentMapDefaultLink (int link)
currentMap->setDefaultLink (link);
}
+void ImageMapsList::drawMap (lout::object::Object *key, core::View *view,
+ core::style::Style *style, int x, int y)
+{
+ ImageMap *map = imageMaps->get (key);
+
+ if (map)
+ map->draw(view, style, x, y);
+}
+
int ImageMapsList::link (object::Object *key, int x, int y)
{
int link = -1;
@@ -139,16 +160,34 @@ Image::~Image()
delete altText;
if (buffer)
buffer->unref ();
+ if (mapKey)
+ delete mapKey;
}
void Image::sizeRequestImpl (core::Requisition *requisition)
{
if (buffer) {
- requisition->width = buffer->getRootWidth ();
- requisition->ascent = buffer->getRootHeight ();
+ if (getStyle ()->height == core::style::LENGTH_AUTO &&
+ core::style::isAbsLength (getStyle ()->width) &&
+ buffer->getRootWidth () > 0) {
+ // preserve aspect ratio when only width is given
+ requisition->width = core::style::absLengthVal (getStyle ()->width);
+ requisition->ascent = buffer->getRootHeight () *
+ requisition->width / buffer->getRootWidth ();
+ } else if (getStyle ()->width == core::style::LENGTH_AUTO &&
+ core::style::isAbsLength (getStyle ()->height) &&
+ buffer->getRootHeight () > 0) {
+ // preserve aspect ratio when only height is given
+ requisition->ascent = core::style::absLengthVal (getStyle ()->height);
+ requisition->width = buffer->getRootWidth () *
+ requisition->ascent / buffer->getRootHeight ();
+ } else {
+ requisition->width = buffer->getRootWidth ();
+ requisition->ascent = buffer->getRootHeight ();
+ }
requisition->descent = 0;
} else {
- if(altText && altText[0]) {
+ if (altText && altText[0]) {
if (altTextWidth == -1)
altTextWidth =
layout->textWidth (getStyle()->font, altText, strlen (altText));
@@ -181,20 +220,21 @@ void Image::sizeAllocateImpl (core::Allocation *allocation)
dx = getStyle()->boxDiffWidth ();
dy = getStyle()->boxDiffHeight ();
#if 0
- printf("boxDiffHeight = %d + %d, buffer=%p\n",
- getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer);
- printf("getContentWidth() = allocation.width - style->boxDiffWidth ()"
- " = %d - %d = %d\n",
- this->allocation.width, getStyle()->boxDiffWidth(),
- this->allocation.width - getStyle()->boxDiffWidth());
- printf("getContentHeight() = getHeight() - style->boxDiffHeight ()"
- " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(),
- this->getHeight() - getStyle()->boxDiffHeight());
+ MSG("boxDiffHeight = %d + %d, buffer=%p\n",
+ getStyle()->boxOffsetY(), getStyle()->boxRestHeight(), buffer);
+ MSG("getContentWidth() = allocation.width - style->boxDiffWidth ()"
+ " = %d - %d = %d\n",
+ this->allocation.width, getStyle()->boxDiffWidth(),
+ this->allocation.width - getStyle()->boxDiffWidth());
+ MSG("getContentHeight() = getHeight() - style->boxDiffHeight ()"
+ " = %d - %d = %d\n", this->getHeight(), getStyle()->boxDiffHeight(),
+ this->getHeight() - getStyle()->boxDiffHeight());
#endif
- if (buffer != NULL &&
- /* It may be, that the image is allocated at zero content size. In this
- * case, we simply wait. */
- getContentWidth () > 0 && getContentHeight () > 0) {
+ if (buffer &&
+ (allocation->width - dx > 0 ||
+ allocation->ascent + allocation->descent - dy > 0)) {
+ // Zero content size : simply wait...
+ // Only one dimension: naturally scale
oldBuffer = buffer;
buffer = oldBuffer->getScaledBuf (allocation->width - dx,
allocation->ascent
@@ -209,35 +249,64 @@ void Image::enterNotifyImpl (core::EventCrossing *event)
currLink = getStyle()->x_link;
if (currLink != -1) {
- (void) emitLinkEnter (currLink, -1, -1, -1);
+ (void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
}
+ Widget::enterNotifyImpl(event);
}
void Image::leaveNotifyImpl (core::EventCrossing *event)
-{
+{
clicking = false;
if (currLink != -1) {
currLink = -1;
- (void) emitLinkEnter (-1, -1, -1, -1);
+ (void) layout->emitLinkEnter (this, -1, -1, -1, -1);
}
+ Widget::leaveNotifyImpl(event);
+}
+
+/*
+ * Return the coordinate relative to the contents.
+ * If the event occurred in the surrounding box, return the value at the
+ * edge of the contents instead.
+ */
+int Image::contentX (core::MousePositionEvent *event)
+{
+ int ret = event->xWidget - getStyle()->boxOffsetX();
+
+ ret = misc::min(getContentWidth(), misc::max(ret, 0));
+ return ret;
+}
+
+int Image::contentY (core::MousePositionEvent *event)
+{
+ int ret = event->yWidget - getStyle()->boxOffsetY();
+
+ ret = misc::min(getContentHeight(), misc::max(ret, 0));
+ return ret;
}
bool Image::motionNotifyImpl (core::EventMotion *event)
{
- if (mapList) {
- /* client-side image map */
- int newLink = mapList->link (mapKey, event->xWidget, event->yWidget);
- if (newLink != currLink) {
- currLink = newLink;
- clicking = false;
- setCursor(newLink == -1 ? core::style::CURSOR_DEFAULT :
- core::style::CURSOR_POINTER);
- (void) emitLinkEnter (newLink, -1, -1, -1);
+ if (mapList || isMap) {
+ int x = contentX(event);
+ int y = contentY(event);
+
+ if (mapList) {
+ /* client-side image map */
+ int newLink = mapList->link (mapKey, x, y);
+ if (newLink != currLink) {
+ currLink = newLink;
+ clicking = false;
+ /* \todo Using MAP/AREA styles would probably be best */
+ setCursor(newLink == -1 ? getStyle()->cursor :
+ core::style::CURSOR_POINTER);
+ (void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
+ }
+ } else if (isMap && currLink != -1) {
+ /* server-side image map */
+ (void) layout->emitLinkEnter (this, currLink, -1, x, y);
}
- } else if (isMap && currLink != -1) {
- /* server-side image map */
- (void) emitLinkEnter (currLink, -1, event->xWidget, event->yWidget);
}
return true;
}
@@ -245,27 +314,29 @@ bool Image::motionNotifyImpl (core::EventMotion *event)
bool Image::buttonPressImpl (core::EventButton *event)
{
bool ret = false;
- currLink = mapList ? mapList->link (mapKey, event->xWidget, event->yWidget):
- getStyle()->x_link;
+
+ currLink = mapList? mapList->link (mapKey, contentX(event),contentY(event)):
+ getStyle()->x_link;
if (event->button == 3){
- (void)emitLinkPress(currLink, getStyle()->x_img, -1,-1,event);
+ (void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
+ event);
ret = true;
} else if (event->button == 1 || currLink != -1){
clicking = true;
ret = true;
}
return ret;
-}
+}
bool Image::buttonReleaseImpl (core::EventButton *event)
{
- currLink = mapList ? mapList->link (mapKey, event->xWidget, event->yWidget):
+ currLink = mapList ? mapList->link (mapKey, contentX(event),contentY(event)):
getStyle()->x_link;
if (clicking) {
- int x = isMap ? event->xWidget : -1;
- int y = isMap ? event->yWidget : -1;
+ int x = isMap ? contentX(event) : -1;
+ int y = isMap ? contentY(event) : -1;
clicking = false;
- emitLinkClick (currLink, getStyle()->x_img, x, y, event);
+ layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
return true;
}
return false;
@@ -292,22 +363,25 @@ void Image::draw (core::View *view, core::Rectangle *area)
intersection.x - dx, intersection.y - dy,
intersection.width, intersection.height);
} else {
- if(altText && altText[0]) {
+ core::View *clippingView;
+
+ if (altText && altText[0]) {
+ core::View *usedView = view;
+
+ clippingView = NULL;
+
if (altTextWidth == -1)
altTextWidth =
layout->textWidth (getStyle()->font, altText, strlen (altText));
-
- core::View *clippingView = NULL, *usedView = view;
- if (allocation.width < altTextWidth ||
- allocation.ascent < getStyle()->font->ascent ||
- allocation.descent < getStyle()->font->descent) {
+
+ if ((getContentWidth() < altTextWidth) ||
+ (getContentHeight() <
+ getStyle()->font->ascent + getStyle()->font->descent)) {
clippingView = usedView =
view->getClippingView (allocation.x + getStyle()->boxOffsetX (),
allocation.y + getStyle()->boxOffsetY (),
- allocation.width
- - getStyle()->boxDiffWidth (),
- allocation.ascent + allocation.descent
- - getStyle()->boxDiffHeight ());
+ getContentWidth(),
+ getContentHeight());
}
usedView->drawText (getStyle()->font, getStyle()->color,
@@ -317,9 +391,21 @@ void Image::draw (core::View *view, core::Rectangle *area)
+ getStyle()->font->ascent,
altText, strlen(altText));
- if(clippingView)
+ if (clippingView)
view->mergeClippingView (clippingView);
}
+ if (mapKey) {
+ clippingView = view->getClippingView (allocation.x +
+ getStyle()->boxOffsetX (),
+ allocation.y +
+ getStyle()->boxOffsetY (),
+ getContentWidth(),
+ getContentHeight());
+ mapList->drawMap(mapKey, clippingView, getStyle(),
+ allocation.x + getStyle()->boxOffsetX (),
+ allocation.y + getStyle()->boxOffsetY ());
+ view->mergeClippingView (clippingView);
+ }
}
/** TODO: draw selection */
@@ -339,11 +425,10 @@ void Image::setBuffer (core::Imgbuf *buffer, bool resize)
if (resize)
queueResize (0, true);
- // If the image has not yet been allocated, or is allocated at zero
- // content size, the first part is useless.
if (wasAllocated () && getContentWidth () > 0 && getContentHeight () > 0) {
- this->buffer =
- buffer->getScaledBuf (getContentWidth (), getContentHeight ());
+ // Only scale when both dimensions are known.
+ this->buffer =
+ buffer->getScaledBuf (getContentWidth (), getContentHeight ());
} else {
this->buffer = buffer;
buffer->ref ();
@@ -358,7 +443,7 @@ void Image::drawRow (int row)
core::Rectangle area;
assert (buffer != NULL);
-
+
buffer->getRowArea (row, &area);
if (area.width && area.height)
queueDrawArea (area.x + getStyle()->boxOffsetX (),
@@ -386,6 +471,8 @@ void Image::setIsMap ()
void Image::setUseMap (ImageMapsList *list, object::Object *key)
{
mapList = list;
+ if (mapKey && mapKey != key)
+ delete mapKey;
mapKey = key;
}
diff --git a/dw/image.hh b/dw/image.hh
index 37c27e0d..a8314d4f 100644
--- a/dw/image.hh
+++ b/dw/image.hh
@@ -38,7 +38,8 @@ private:
public:
ImageMap ();
~ImageMap ();
-
+
+ void draw (core::View *view, core::style::Style *style, int x, int y);
void add (core::Shape *shape, int link);
void setDefaultLink (int link) { defaultLink = link; };
int link (int x, int y);
@@ -47,7 +48,7 @@ private:
lout::container::typed::HashTable <lout::object::Object, ImageMap>
*imageMaps;
ImageMap *currentMap;
-
+
public:
ImageMapsList ();
~ImageMapsList ();
@@ -55,6 +56,8 @@ public:
void startNewMap (lout::object::Object *key);
void addShapeToCurrentMap (core::Shape *shape, int link);
void setCurrentMapDefaultLink (int link);
+ void drawMap(lout::object::Object *key, core::View *view,
+ core::style::Style *style, int x, int y);
int link (lout::object::Object *key, int x, int y);
};
@@ -68,7 +71,7 @@ public:
* <h3>Signals</h3>
*
* For image maps, dw::Image uses the signals defined in
- * dw::core::Widget::LinkReceiver. For client side image maps, -1 is
+ * dw::core::Layout::LinkReceiver. For client side image maps, -1 is
* passed for the coordinates, for server side image maps, the respective
* coordinates are used. See section "Image Maps" below.
*
@@ -89,8 +92,8 @@ public:
*
* dw::ImageMapsList::addShapeToCurrentMap adds a shape to the current
* map. The \em link argument is a number, which is later passed to
- * the dw::core::Widget::LinkReceiver.
- *
+ * the dw::core::Layout::LinkReceiver.
+ *
* This map list is then, together with the key for the image, passed to
* dw::Image::setUseMap. For HTML, a URL with the value of the "ismap"
* attribute of \<IMG\> should be used.
@@ -103,13 +106,13 @@ public:
* Currently, only maps defined in the same document as the image may be
* used, since the dw::ImageMapsList is stored in the HTML link block, and
* contains only the image maps defined in the document.
- *
+ *
* <h4>Server Side %Image Maps</h4>
- *
+ *
* To use images for server side image maps, you must call
* dw::Image::setIsMap, and the dw::Image::style must contain a valid link
* (dw::core::style::Style::x_link). After this, motions and clicks are
- * delegated to dw::core::Widget::LinkReceiver.
+ * delegated to dw::core::Layout::LinkReceiver.
*
* \sa\ref dw-images-and-backgrounds
*/
@@ -129,15 +132,17 @@ protected:
void sizeRequestImpl (core::Requisition *requisition);
void sizeAllocateImpl (core::Allocation *allocation);
- void draw (core::View *view, core::Rectangle *area);
+ void draw (core::View *view, core::Rectangle *area);
bool buttonPressImpl (core::EventButton *event);
bool buttonReleaseImpl (core::EventButton *event);
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
bool motionNotifyImpl (core::EventMotion *event);
+ int contentX (core::MousePositionEvent *event);
+ int contentY (core::MousePositionEvent *event);
- //core::Iterator *iterator (Content::Type mask, bool atEnd);
+ //core::Iterator *iterator (Content::Type mask, bool atEnd);
public:
static int CLASS_ID;
@@ -145,7 +150,7 @@ public:
Image(const char *altText);
~Image();
- core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
inline core::Imgbuf *getBuffer () { return buffer; }
void setBuffer (core::Imgbuf *buffer, bool resize = false);
@@ -154,6 +159,13 @@ public:
void setIsMap ();
void setUseMap (ImageMapsList *list, Object *key);
+
+ /* This is a hack for the perhaps frivolous feature of drawing image map
+ * shapes when there is no image to display. If the map is defined after
+ * an image using an image map, and the actual image data has not been
+ * loaded, tell the image to redraw.
+ */
+ void forceMapRedraw () { if (mapKey && ! buffer) queueDraw (); };
};
} // namespace dw
diff --git a/dw/imgbuf.hh b/dw/imgbuf.hh
index 8948bbef..150bf165 100644
--- a/dw/imgbuf.hh
+++ b/dw/imgbuf.hh
@@ -8,13 +8,11 @@
namespace dw {
namespace core {
-using namespace lout;
-
/**
- * \brief The platform independant interface for image buffers.
+ * \brief The platform independent interface for image buffers.
*
* %Image buffers depend on the platform (see \ref dw-images-and-backgrounds),
- * but have this general, platform independant interface. The purpose of
+ * but have this general, platform independent interface. The purpose of
* an image buffer is
*
* <ol>
@@ -53,16 +51,16 @@ using namespace lout;
*
*
* <h3>Scaling</h3>
- *
+ *
* The buffer with the original size, which was created by
* dw::core::Platform::createImgbuf, is called root buffer. Imgbuf provides
* the ability to scale buffers. Generally, both root buffers, as well as
* scaled buffers, may be shared, memory management is done by reference
* counters.
- *
+ *
* Via dw::core::Imgbuf::getScaledBuf, you can retrieve a scaled buffer.
* Generally, something like this must work always, in an efficient way:
- *
+ *
* \code
* dw::core::Imgbuf *curBuf, *oldBuf;
* int width, heigt,
@@ -71,15 +69,15 @@ using namespace lout;
* curBuf = oldBuf->getScaledBuf(oldBuf, width, height);
* oldBuf->unref();
* \endcode
- *
+ *
* \em oldBuf may both be a root buffer, or a scaled buffer.
- *
+ *
* The root buffer keeps a list of all children, and all methods
* operating on the image data (dw::core::Imgbuf::copyRow and
* dw::core::Imgbuf::setCMap) are delegated to the scaled buffers, when
* processed, and inherited, when a new scaled buffer is created. This
* means, that they must only be performed for the root buffer.
- *
+ *
* A possible implementation could be (dw::fltk::FltkImgbuf does it this way):
*
* <ul>
@@ -92,7 +90,7 @@ using namespace lout;
* <li> Otherwise, if this buffer has already been scaled to the given
* size, return this scaled buffer, with an increased reference
* counter.
- *
+ *
* <li> Otherwise, return a new scaled buffer with reference counter 1.
* </ul>
*
@@ -120,7 +118,7 @@ using namespace lout;
* \code
* dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
* dw::core::Layout *layout = new dw::core::Layout (platform);
- *
+ *
* dw::core::Imgbuf *rootbuf =
* layout->createImgbuf (dw::core::Imgbuf::RGB, 100, 100);
* dw::core::Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
@@ -133,12 +131,12 @@ using namespace lout;
* the scaled buffer, it is deleted, and after it, the root buffer.
*
* <h3>Drawing</h3>
- *
+ *
* dw::core::Imgbuf provides no methods for drawing, instead, this is
* done by the views (implementation of dw::core::View).
*
* There are two situations, when drawing is necessary:
- *
+ *
* <ol>
* <li> To react on expose events, the function dw::core::View::drawImage
* should be used, with the following parameters:
@@ -149,7 +147,7 @@ using namespace lout;
* <li> the region within the image, which should be displayed (\em x,
* \em y, \em width, \em height).
* </ul>
- *
+ *
* <li> When a row has been copied, it has to be drawn. To determine the
* area, which has to be drawn, the dw::core::Imgbuf::getRowArea
* should be used. The result can then passed
@@ -158,7 +156,7 @@ using namespace lout;
*
* \sa \ref dw-images-and-backgrounds
*/
-class Imgbuf: public object::Object, public lout::signal::ObservedObject
+class Imgbuf: public lout::object::Object, public lout::signal::ObservedObject
{
public:
enum Type { RGB, RGBA, GRAY, INDEXED, INDEXED_ALPHA };
diff --git a/dw/iterator.cc b/dw/iterator.cc
index 39e09d41..e9431e9b 100644
--- a/dw/iterator.cc
+++ b/dw/iterator.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -23,6 +22,8 @@
#include "core.hh"
#include <limits.h>
+using namespace lout;
+
namespace dw {
namespace core {
@@ -57,7 +58,7 @@ bool Iterator::equals (Object *other)
/**
* \brief Delete the iterator.
*
- * The desctructor is hidden, implementations may use optimizations for
+ * The destructor is hidden, implementations may use optimizations for
* the allocation. (Will soon be the case for dw::core::EmptyIteratorFactory.)
*/
void Iterator::unref ()
@@ -146,7 +147,7 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
// |----------------------------|
// width
//
- // Therefor, we the region smaller, so that the region will be
+ // Therefore, we make the region smaller, so that the region will be
// displayed like this:
//
// ,-- alloc1
@@ -159,7 +160,7 @@ void Iterator::scrollTo (Iterator *it1, Iterator *it2, int start, int end,
// |----------|
// width
//
-
+
/** \todo Changes in the viewport size, until the idle function is
* called, are not regarded. */
@@ -210,7 +211,7 @@ int EmptyIterator::compareTo (misc::Comparable *other)
if (content.type == otherIt->content.type)
return 0;
- else if(content.type == Content::START)
+ else if (content.type == Content::START)
return -1;
else
return +1;
@@ -468,7 +469,7 @@ DeepIterator::DeepIterator (Iterator *it)
// since an DeepIterator should never return widgets.
if (it->getContent()->type == Content::WIDGET) {
Iterator *it2;
-
+
// The second argument of searchDownward is actually a matter of
// taste :-)
if ((it2 = searchDownward (it, mask, false)) ||
@@ -484,10 +485,10 @@ DeepIterator::DeepIterator (Iterator *it)
hasContents = false;
}
}
-
+
//DEBUG_MSG (1, " => %s\n", a_Dw_iterator_text (it));
- if(hasContents) {
+ if (hasContents) {
// If this widget has parents, we must construct appropriate iterators.
//
// \todo There may be a faster way instead of iterating through the
@@ -509,7 +510,7 @@ DeepIterator::DeepIterator (Iterator *it)
break;
}
}
-
+
stack.put (it, thisLevel);
content = *(it->getContent());
}
@@ -552,10 +553,10 @@ int DeepIterator::compareTo (misc::Comparable *other)
while (stack.get(level)->getWidget ()
!= otherDeepIterator->stack.get(level)->getWidget ())
level--;
-
+
return stack.get(level)->compareTo (otherDeepIterator->stack.get(level));
}
-
+
DeepIterator *DeepIterator::createVariant(Iterator *it)
{
/** \todo Not yet implemented, and actually not yet needed very much. */
@@ -574,7 +575,7 @@ bool DeepIterator::isEmpty () {
bool DeepIterator::next ()
{
Iterator *it = stack.getTop ();
-
+
if (it->next ()) {
if (it->getContent()->type == Content::WIDGET) {
// Widget: new iterator on stack, to search in this widget.
@@ -596,7 +597,7 @@ bool DeepIterator::next ()
content.type = Content::END;
return false;
}
- }
+ }
}
/**
@@ -607,7 +608,7 @@ bool DeepIterator::next ()
bool DeepIterator::prev ()
{
Iterator *it = stack.getTop ();
-
+
if (it->prev ()) {
if (it->getContent()->type == Content::WIDGET) {
// Widget: new iterator on stack, to search in this widget.
@@ -629,7 +630,7 @@ bool DeepIterator::prev ()
content.type = Content::START;
return false;
}
- }
+ }
}
// -----------------
@@ -679,7 +680,7 @@ bool CharIterator::next ()
if (ch == START || it->getContent()->type == Content::BREAK ||
(it->getContent()->type == Content::TEXT &&
it->getContent()->text[pos] == 0)) {
- if(it->next()) {
+ if (it->next()) {
if (it->getContent()->type == Content::BREAK)
ch = '\n';
else { // if (it->getContent()->type == Content::TEXT)
@@ -695,7 +696,7 @@ bool CharIterator::next ()
ch = END;
return false;
}
- } else if(ch == END)
+ } else if (ch == END)
return false;
else {
// at this point, it->getContent()->type == Content::TEXT
@@ -708,16 +709,16 @@ bool CharIterator::next ()
return next ();
}
}
-
+
return true;
- }
+ }
}
bool CharIterator::prev ()
{
if (ch == END || it->getContent()->type == Content::BREAK ||
(it->getContent()->type == Content::TEXT && pos == 0)) {
- if(it->prev()) {
+ if (it->prev()) {
if (it->getContent()->type == Content::BREAK)
ch = '\n';
else { // if (it->getContent()->type == Content::TEXT)
@@ -739,14 +740,14 @@ bool CharIterator::prev ()
ch = START;
return false;
}
- } else if(ch == START)
+ } else if (ch == START)
return false;
else {
// at this point, it->getContent()->type == Content::TEXT
pos--;
ch = it->getContent()->text[pos];
return true;
- }
+ }
}
void CharIterator::highlight (CharIterator *it1, CharIterator *it2,
diff --git a/dw/iterator.hh b/dw/iterator.hh
index 605217ec..838d66a1 100644
--- a/dw/iterator.hh
+++ b/dw/iterator.hh
@@ -16,7 +16,7 @@ namespace core {
*
* \sa dw::core::Widget::iterator
*/
-class Iterator: public object::Object, public misc::Comparable
+class Iterator: public lout::object::Object, public lout::misc::Comparable
{
protected:
Iterator(Widget *widget, Content::Type mask, bool atEnd);
@@ -65,10 +65,10 @@ public:
/**
* \brief Shrink highlighted region to no longer contain the
* current content.
- *
- * The direction parameter indicates whether the highlighted region should be
- * reduced from the start (direction > 0) or from the end (direction < 0).
- * If direction is 0 all content is unhighlighted.
+ *
+ * The direction parameter indicates whether the highlighted region should
+ * be reduced from the start (direction > 0) or from the end
+ * (direction < 0). If direction is 0 all content is unhighlighted.
*/
virtual void unhighlight (int direction, HighlightLayer layer) = 0;
@@ -80,7 +80,7 @@ public:
* DwIterator::highlight().
*/
virtual void getAllocation (int start, int end, Allocation *allocation) = 0;
-
+
inline Iterator *cloneIterator () { return (Iterator*)clone(); }
static void scrollTo (Iterator *it1, Iterator *it2, int start, int end,
@@ -96,12 +96,12 @@ class EmptyIterator: public Iterator
{
private:
EmptyIterator (EmptyIterator &it);
-
+
public:
EmptyIterator (Widget *widget, Content::Type mask, bool atEnd);
-
- object::Object *clone();
- int compareTo(misc::Comparable *other);
+
+ lout::object::Object *clone();
+ int compareTo(lout::misc::Comparable *other);
bool next ();
bool prev ();
void highlight (int start, int end, HighlightLayer layer);
@@ -119,14 +119,14 @@ class TextIterator: public Iterator
private:
/** May be NULL, in this case, the next is skipped. */
const char *text;
-
+
TextIterator (TextIterator &it);
-
+
public:
TextIterator (Widget *widget, Content::Type mask, bool atEnd,
const char *text);
-
- int compareTo(misc::Comparable *other);
+
+ int compareTo(lout::misc::Comparable *other);
bool next ();
bool prev ();
@@ -142,13 +142,13 @@ public:
* iterators do not have the limitation, that iteration is only done within
* a widget, instead, child widgets are iterated through recursively.
*/
-class DeepIterator: public object::Object, public misc::Comparable
+class DeepIterator: public lout::object::Object, public lout::misc::Comparable
{
private:
- class Stack: public container::typed::Vector<Iterator>
+ class Stack: public lout::container::typed::Vector<Iterator>
{
public:
- inline Stack (): container::typed::Vector<Iterator> (4, false) { }
+ inline Stack (): lout::container::typed::Vector<Iterator> (4, false) { }
~Stack ();
inline Iterator *getTop () { return get (size () - 1); }
inline void push (Iterator *it) { put(it, -1); }
@@ -168,14 +168,14 @@ private:
inline DeepIterator () { }
-public:
+public:
DeepIterator(Iterator *it);
~DeepIterator();
- object::Object *clone ();
+ lout::object::Object *clone ();
DeepIterator *createVariant(Iterator *it);
- inline Iterator *getTopIterator () { return stack.getTop(); }
+ inline Iterator *getTopIterator () { return stack.getTop(); }
inline Content *getContent () { return &content; }
bool isEmpty ();
@@ -183,12 +183,12 @@ public:
bool next ();
bool prev ();
inline DeepIterator *cloneDeepIterator() { return (DeepIterator*)clone(); }
- int compareTo(misc::Comparable *other);
+ int compareTo(lout::misc::Comparable *other);
/**
* \brief Highlight a part of the current content.
*
- * Unhighlight the current content by passing -1 as start (see also
+ * Unhighlight the current content by passing -1 as start (see also
* (dw::core::Iterator::unhighlight). For text, start and end define the
* characters, otherwise, the shape is defined as [0, 1], i.e. for
* highlighting a whole dw::core::Content, pass 0 and >= 1.
@@ -216,10 +216,12 @@ public:
start, end, hpos, vpos); }
};
-class CharIterator: public object::Object, public misc::Comparable
+class CharIterator: public lout::object::Object, public lout::misc::Comparable
{
public:
- enum { START = -1, END = -2 };
+ // START and END must not clash with any char value
+ // neither for signed nor unsigned char.
+ enum { START = 257, END = 258 };
private:
DeepIterator *it;
@@ -231,8 +233,8 @@ public:
CharIterator (Widget *widget);
~CharIterator ();
- object::Object *clone();
- int compareTo(misc::Comparable *other);
+ lout::object::Object *clone();
+ int compareTo(lout::misc::Comparable *other);
bool next ();
bool prev ();
diff --git a/dw/layout.cc b/dw/layout.cc
index 060c182f..d8b96d55 100644
--- a/dw/layout.cc
+++ b/dw/layout.cc
@@ -14,17 +14,18 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core.hh"
+#include "../lout/msg.h"
#include "../lout/debug.hh"
#include "../lout/misc.hh"
+using namespace lout;
using namespace lout::container;
using namespace lout::object;
@@ -38,8 +39,8 @@ void Layout::Receiver::canvasSizeChanged (int width, int ascent, int descent)
// ----------------------------------------------------------------------
bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
- int signalNo,
- int argc, Object **argv)
+ int signalNo, int argc,
+ lout::object::Object **argv)
{
Receiver *layoutReceiver = (Receiver*)receiver;
@@ -54,7 +55,7 @@ bool Layout::Emitter::emitToReceiver (lout::signal::Receiver *receiver,
misc::assertNotReached ();
}
- return false;
+ return false;
}
void Layout::Emitter::emitCanvasSizeChanged (int width,
@@ -65,6 +66,110 @@ void Layout::Emitter::emitCanvasSizeChanged (int width,
emitVoid (CANVAS_SIZE_CHANGED, 3, argv);
}
+// ----------------------------------------------------------------------
+
+bool Layout::LinkReceiver::enter (Widget *widget, int link, int img,
+ int x, int y)
+{
+ return false;
+}
+
+bool Layout::LinkReceiver::press (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ return false;
+}
+
+bool Layout::LinkReceiver::release (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ return false;
+}
+
+bool Layout::LinkReceiver::click (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ return false;
+}
+
+// ----------------------------------------------------------------------
+
+bool Layout::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver,
+ int signalNo, int argc,
+ lout::object::Object **argv)
+{
+ LinkReceiver *linkReceiver = (LinkReceiver*)receiver;
+
+ switch (signalNo) {
+ case ENTER:
+ return linkReceiver->enter ((Widget*)argv[0],
+ ((Integer*)argv[1])->getValue (),
+ ((Integer*)argv[2])->getValue (),
+ ((Integer*)argv[3])->getValue (),
+ ((Integer*)argv[4])->getValue ());
+
+ case PRESS:
+ return linkReceiver->press ((Widget*)argv[0],
+ ((Integer*)argv[1])->getValue (),
+ ((Integer*)argv[2])->getValue (),
+ ((Integer*)argv[3])->getValue (),
+ ((Integer*)argv[4])->getValue (),
+ (EventButton*)argv[5]);
+
+ case RELEASE:
+ return linkReceiver->release ((Widget*)argv[0],
+ ((Integer*)argv[1])->getValue (),
+ ((Integer*)argv[2])->getValue (),
+ ((Integer*)argv[3])->getValue (),
+ ((Integer*)argv[4])->getValue (),
+ (EventButton*)argv[5]);
+
+ case CLICK:
+ return linkReceiver->click ((Widget*)argv[0],
+ ((Integer*)argv[1])->getValue (),
+ ((Integer*)argv[2])->getValue (),
+ ((Integer*)argv[3])->getValue (),
+ ((Integer*)argv[4])->getValue (),
+ (EventButton*)argv[5]);
+
+ default:
+ misc::assertNotReached ();
+ }
+ return false;
+}
+
+bool Layout::LinkEmitter::emitEnter (Widget *widget, int link, int img,
+ int x, int y)
+{
+ Integer ilink (link), iimg (img), ix (x), iy (y);
+ Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy };
+ return emitBool (ENTER, 5, argv);
+}
+
+bool Layout::LinkEmitter::emitPress (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ Integer ilink (link), iimg (img), ix (x), iy (y);
+ Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
+ return emitBool (PRESS, 6, argv);
+}
+
+bool Layout::LinkEmitter::emitRelease (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ Integer ilink (link), iimg (img), ix (x), iy (y);
+ Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
+ return emitBool (RELEASE, 6, argv);
+}
+
+bool Layout::LinkEmitter::emitClick (Widget *widget, int link, int img,
+ int x, int y, EventButton *event)
+{
+ Integer ilink (link), iimg (img), ix (x), iy (y);
+ Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
+ return emitBool (CLICK, 6, argv);
+}
+
// ---------------------------------------------------------------------
Layout::Anchor::~Anchor ()
@@ -77,7 +182,7 @@ Layout::Anchor::~Anchor ()
Layout::Layout (Platform *platform)
{
this->platform = platform;
- views = new container::typed::List <View> (true);
+ view = NULL;
topLevel = NULL;
widgetAtPoint = NULL;
@@ -87,9 +192,11 @@ Layout::Layout (Platform *platform)
cursor = style::CURSOR_DEFAULT;
canvasWidth = canvasAscent = canvasDescent = 0;
-
+
usesViewport = false;
scrollX = scrollY = 0;
+ viewportWidth = viewportHeight = 0;
+ hScrollbarThickness = vScrollbarThickness = 0;
requestedAnchor = NULL;
scrollIdleId = -1;
@@ -120,7 +227,7 @@ Layout::~Layout ()
if (topLevel)
delete topLevel;
delete platform;
- delete views;
+ delete view;
delete anchorsTable;
delete textZone;
}
@@ -128,7 +235,7 @@ Layout::~Layout ()
void Layout::addWidget (Widget *widget)
{
if (topLevel) {
- fprintf (stderr, "widget already set\n");
+ MSG_WARN("widget already set\n");
return;
}
@@ -154,13 +261,10 @@ void Layout::removeWidget ()
canvasWidth = canvasAscent = canvasDescent = 0;
scrollX = scrollY = 0;
- for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) {
- View *view = it.getNext ();
- view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
- if (view->usesViewport ())
- view->setViewportSize (viewportWidth, viewportHeight, 0, 0);
- view->queueDrawTotal ();
- }
+ view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
+ if (view->usesViewport ())
+ view->setViewportSize (viewportWidth, viewportHeight, 0, 0);
+ view->queueDrawTotal ();
setAnchor (NULL);
updateAnchor ();
@@ -192,7 +296,10 @@ void Layout::setWidget (Widget *widget)
*/
void Layout::attachView (View *view)
{
- views->append (view);
+ if (this->view)
+ MSG_ERR("attachView: Multiple views for layout!\n");
+
+ this->view = view;
platform->attachView (view);
/*
@@ -234,11 +341,12 @@ void Layout::attachView (View *view)
void Layout::detachView (View *view)
{
+ if (this->view != view)
+ MSG_ERR("detachView: this->view: %p view %p\n", this->view, view);
+
view->setLayout (NULL);
platform->detachView (view);
-
- views->detachRef (view);
-
+ this->view = NULL;
/**
* \todo Actually, viewportMarkerWidthDiff and
* viewportMarkerHeightDiff have to be recalculated here, since the
@@ -247,8 +355,14 @@ void Layout::detachView (View *view)
*/
}
+void Layout::scroll(ScrollCommand cmd)
+{
+ if (view->usesViewport ())
+ view->scroll(cmd);
+}
+
/**
- * \brief Scrolls all viewports, so that the region [x, y, width, height]
+ * \brief Scrolls all viewports, so that the region [x, y, width, height]
* is seen, according to hpos and vpos.
*/
void Layout::scrollTo (HPosition hpos, VPosition vpos,
@@ -262,8 +376,8 @@ void Layout::scrollTo0 (HPosition hpos, VPosition vpos,
bool scrollingInterrupted)
{
if (usesViewport) {
- //printf ("scrollTo (%d, %d, %s)\n",
- // x, y, scrollingInterrupted ? "true" : "false");
+ _MSG("scrollTo (%d, %d, %s)\n",
+ x, y, scrollingInterrupted ? "true" : "false");
scrollTargetHpos = hpos;
scrollTargetVpos = vpos;
@@ -271,12 +385,12 @@ void Layout::scrollTo0 (HPosition hpos, VPosition vpos,
scrollTargetY = y;
scrollTargetWidth = width;
scrollTargetHeight = height;
-
+
if (scrollIdleId == -1) {
scrollIdleId = platform->addIdle (&Layout::scrollIdle);
scrollIdleNotInterrupted = true;
}
-
+
scrollIdleNotInterrupted =
scrollIdleNotInterrupted || !scrollingInterrupted;
}
@@ -334,11 +448,7 @@ void Layout::scrollIdle ()
if (xChanged || yChanged) {
adjustScrollPos ();
- for (container::typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *thisView = it.getNext();
- thisView->scrollTo (scrollX, scrollY);
- }
+ view->scrollTo (scrollX, scrollY);
}
scrollIdleId = -1;
@@ -354,7 +464,7 @@ void Layout::adjustScrollPos ()
canvasAscent + canvasDescent - (viewportHeight - hScrollbarThickness));
scrollY = misc::max (scrollY, 0);
- //printf("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY);
+ _MSG("adjustScrollPos: scrollX=%d scrollY=%d\n", scrollX, scrollY);
}
bool Layout::calcScrollInto (int requestedValue, int requestedSize,
@@ -414,7 +524,7 @@ void Layout::draw (View *view, Rectangle *area)
*/
void Layout::setAnchor (const char *anchor)
{
- //printf ("setAnchor (%s)\n", anchor);
+ _MSG("setAnchor (%s)\n", anchor);
if (requestedAnchor)
delete requestedAnchor;
@@ -450,7 +560,7 @@ char *Layout::addAnchor (Widget *widget, const char* name, int y)
void Layout::changeAnchor (Widget *widget, char* name, int y)
{
- String key (name);
+ String key (name);
Anchor *anchor = anchorsTable->get (&key);
assert (anchor);
assert (anchor->widget == widget);
@@ -488,11 +598,7 @@ void Layout::setCursor (style::Cursor cursor)
{
if (cursor != this->cursor) {
this->cursor = cursor;
-
- for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) {
- View *view = it.getNext ();
- view->setCursor (cursor);
- }
+ view->setCursor (cursor);
}
}
@@ -514,17 +620,13 @@ void Layout::updateBgColor ()
bgColor = topLevel->getStyle()->backgroundColor;
else
bgColor = NULL;
-
- for (typed::Iterator <View> it = views->iterator (); it.hasNext (); ) {
- View *view = it.getNext ();
- view->setBgColor (bgColor);
- }
+ view->setBgColor (bgColor);
}
void Layout::resizeIdle ()
{
//static int calls = 0;
- //printf(" Layout::resizeIdle calls = %d\n", ++calls);
+ //MSG(" Layout::resizeIdle calls = %d\n", ++calls);
while (resizeIdleId != -1) {
// Reset already here, since in this function, queueResize() may be
@@ -550,12 +652,9 @@ void Layout::resizeIdle ()
emitter.emitCanvasSizeChanged (
canvasWidth, canvasAscent, canvasDescent);
- // Tell the views about the new world size.
- for (typed::Iterator <View> it = views->iterator (); it.hasNext ();) {
- View *view = it.getNext ();
- view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
- // view->queueDrawTotal (false);
- }
+ // Tell the view about the new world size.
+ view->setCanvasSize (canvasWidth, canvasAscent, canvasDescent);
+ // view->queueDrawTotal (false);
if (usesViewport) {
int actualHScrollbarThickness =
@@ -564,7 +663,7 @@ void Layout::resizeIdle ()
(canvasAscent + canvasDescent > viewportHeight) ?
vScrollbarThickness : 0;
- if (!canvasHeightGreater &&
+ if (!canvasHeightGreater &&
canvasAscent + canvasDescent
> viewportHeight - actualHScrollbarThickness) {
canvasHeightGreater = true;
@@ -573,14 +672,10 @@ void Layout::resizeIdle ()
}
// Set viewport sizes.
- for (typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *view = it.getNext ();
- if (view->usesViewport ())
- view->setViewportSize (viewportWidth, viewportHeight,
- actualHScrollbarThickness,
- actualVScrollbarThickness);
- }
+ if (view->usesViewport ())
+ view->setViewportSize (viewportWidth, viewportHeight,
+ actualHScrollbarThickness,
+ actualVScrollbarThickness);
}
}
@@ -592,7 +687,7 @@ void Layout::resizeIdle ()
}
void Layout::setSizeHints ()
-{
+{
if (topLevel) {
topLevel->setWidth (viewportWidth
- (canvasHeightGreater ? vScrollbarThickness : 0));
@@ -611,11 +706,7 @@ void Layout::queueDraw (int x, int y, int width, int height)
if (area.isEmpty ()) return;
- for (container::typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *view = it.getNext ();
- view->queueDraw (&area);
- }
+ view->queueDraw (&area);
}
void Layout::queueDrawExcept (int x, int y, int width, int height,
@@ -623,15 +714,15 @@ void Layout::queueDrawExcept (int x, int y, int width, int height,
if (x == ex && y == ey && width == ewidth && height == eheight)
return;
-
+
// queueDraw() the four rectangles within rectangle (x, y, width, height)
// around rectangle (ex, ey, ewidth, eheight).
// Some or all of these may be empty.
- // upper left corner of the intersection rectangle
+ // upper left corner of the intersection rectangle
int ix1 = misc::max (x, ex);
int iy1 = misc::max (y, ey);
- // lower right corner of the intersection rectangle
+ // lower right corner of the intersection rectangle
int ix2 = misc::min (x + width, ex + ewidth);
int iy2 = misc::min (y + height, ey + eheight);
@@ -644,11 +735,7 @@ void Layout::queueDrawExcept (int x, int y, int width, int height,
void Layout::queueResize ()
{
if (resizeIdleId == -1) {
- for (container::typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *view = it.getNext ();
- view->cancelQueueDraw ();
- }
+ view->cancelQueueDraw ();
resizeIdleId = platform->addIdle (&Layout::resizeIdle);
}
@@ -659,10 +746,10 @@ void Layout::queueResize ()
bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
int x, int y, ButtonState state, int button)
-
+
{
EventButton event;
-
+
moveToWidgetAtPoint (x, y, state);
event.xCanvas = x;
@@ -670,7 +757,7 @@ bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
event.state = state;
event.button = button;
event.numPressed = numPressed;
-
+
return processMouseEvent (&event, type, true);
}
@@ -683,13 +770,13 @@ bool Layout::buttonEvent (ButtonEventType type, View *view, int numPressed,
bool Layout::motionNotify (View *view, int x, int y, ButtonState state)
{
EventButton event;
-
+
moveToWidgetAtPoint (x, y, state);
event.xCanvas = x;
event.yCanvas = y;
event.state = state;
-
+
return processMouseEvent (&event, MOTION_NOTIFY, true);
}
@@ -706,7 +793,7 @@ void Layout::enterNotify (View *view, int x, int y, ButtonState state)
lastWidget = widgetAtPoint;
moveToWidgetAtPoint (x, y, state);
- if(widgetAtPoint) {
+ if (widgetAtPoint) {
event.state = state;
event.lastWidget = lastWidget;
event.currentWidget = widgetAtPoint;
@@ -727,7 +814,7 @@ void Layout::leaveNotify (View *view, ButtonState state)
lastWidget = widgetAtPoint;
moveOutOfView (state);
- if(lastWidget) {
+ if (lastWidget) {
event.state = state;
event.lastWidget = lastWidget;
event.currentWidget = widgetAtPoint;
@@ -740,8 +827,8 @@ void Layout::leaveNotify (View *view, ButtonState state)
*/
Widget *Layout::getWidgetAtPoint (int x, int y)
{
- //_MSG ("------------------------------------------------------------\n");
- //_MSG ("widget at (%d, %d)\n", x, y);
+ _MSG ("------------------------------------------------------------\n");
+ _MSG ("widget at (%d, %d)\n", x, y);
if (topLevel)
return topLevel->getWidgetAtPoint (x, y, 0);
else
@@ -761,11 +848,11 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
EventCrossing crossingEvent;
if (newWidgetAtPoint != widgetAtPoint) {
- // The mouse pointer has been moved into another widget.
+ // The mouse pointer has been moved into another widget.
if (newWidgetAtPoint && widgetAtPoint)
ancestor =
newWidgetAtPoint->getNearestCommonAncestor (widgetAtPoint);
- else if(newWidgetAtPoint)
+ else if (newWidgetAtPoint)
ancestor = newWidgetAtPoint->getTopLevel ();
else
ancestor = widgetAtPoint->getTopLevel ();
@@ -777,7 +864,7 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
trackLen++;
trackLen++; // for the ancestor
- if(newWidgetAtPoint)
+ if (newWidgetAtPoint)
// second part
for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
trackLen++;
@@ -789,7 +876,7 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
for (w = widgetAtPoint; w != ancestor; w = w->getParent ())
track[i++] = w;
track[i++] = ancestor;
- if(newWidgetAtPoint) {
+ if (newWidgetAtPoint) {
/* second part */
i = trackLen - 1;
for (w = newWidgetAtPoint; w != ancestor; w = w->getParent ())
@@ -801,13 +888,13 @@ void Layout::moveToWidget (Widget *newWidgetAtPoint, ButtonState state)
crossingEvent.state = state;
crossingEvent.currentWidget = widgetAtPoint; // ???
crossingEvent.lastWidget = widgetAtPoint; // ???
-
+
if (i != 0)
track[i]->enterNotify (&crossingEvent);
if (i != trackLen - 1)
track[i]->leaveNotify (&crossingEvent);
}
-
+
delete[] track;
widgetAtPoint = newWidgetAtPoint;
@@ -827,7 +914,7 @@ bool Layout::processMouseEvent (MousePositionEvent *event,
Widget *widget;
for (widget = widgetAtPoint; widget; widget = widget->getParent ()) {
- if(!mayBeSuppressed || widget->isButtonSensitive ()) {
+ if (!mayBeSuppressed || widget->isButtonSensitive ()) {
event->xWidget = event->xCanvas - widget->getAllocation()->x;
event->yWidget = event->yCanvas - widget->getAllocation()->y;
@@ -846,6 +933,10 @@ bool Layout::processMouseEvent (MousePositionEvent *event,
}
}
}
+ if (type == BUTTON_PRESS)
+ return emitLinkPress (NULL, -1, -1, -1, -1, (EventButton*)event);
+ else if (type == BUTTON_RELEASE)
+ return emitLinkRelease(NULL, -1, -1, -1, -1, (EventButton*)event);
return false;
}
@@ -860,14 +951,6 @@ void Layout::scrollPosChanged (View *view, int x, int y)
if (x != scrollX || y != scrollY) {
scrollX = x;
scrollY = y;
-
- // Tell all views about the scrolling position, except the caller.
- for (container::typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *thisView = it.getNext();
- if(view != thisView && thisView->usesViewport ())
- thisView->scrollTo (scrollX, scrollY);
- }
setAnchor (NULL);
updateAnchor ();
@@ -880,12 +963,12 @@ void Layout::scrollPosChanged (View *view, int x, int y)
*/
void Layout::viewportSizeChanged (View *view, int width, int height)
{
- //printf("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n",
- // viewportWidth, viewportHeight, width, height);
+ _MSG("Layout::viewportSizeChanged w=%d h=%d new_w=%d new_h=%d\n",
+ viewportWidth, viewportHeight, width, height);
/* If the width has become higher, we test again, whether the vertical
* scrollbar (so to speak) can be hidden again. */
- if(usesViewport && width > viewportWidth)
+ if (usesViewport && width > viewportWidth)
canvasHeightGreater = false;
/* if size changes, redraw this view.
@@ -897,21 +980,6 @@ void Layout::viewportSizeChanged (View *view, int width, int height)
viewportHeight = height;
setSizeHints ();
-
- int actualHScrollbarThickness =
- (canvasWidth > viewportWidth) ? hScrollbarThickness : 0;
- int actualVScrollbarThickness =
- (canvasAscent + canvasDescent > viewportWidth) ? vScrollbarThickness : 0;
-
- /* Tell all views about the size, except the caller. */
- for (container::typed::Iterator <View> it = views->iterator ();
- it.hasNext (); ) {
- View *thisView = it.getNext();
- if(view != thisView && thisView->usesViewport ())
- thisView->setViewportSize (viewportWidth, viewportHeight,
- actualHScrollbarThickness,
- actualVScrollbarThickness);
- }
}
} // namespace dw
diff --git a/dw/layout.hh b/dw/layout.hh
index 13b8f312..304cf166 100644
--- a/dw/layout.hh
+++ b/dw/layout.hh
@@ -13,7 +13,7 @@ namespace core {
*
* \sa\ref dw-overview, \ref dw-layout-widgets, \ref dw-layout-views
*/
-class Layout: public object::Object
+class Layout: public lout::object::Object
{
friend class Widget;
@@ -29,6 +29,78 @@ public:
virtual void canvasSizeChanged (int width, int ascent, int descent);
};
+ class LinkReceiver: public lout::signal::Receiver
+ {
+ public:
+ /**
+ * \brief Called, when a link is entered, left, or the position has
+ * changed.
+ *
+ * When a link is entered, this method is called with the respective
+ * arguments. When a link is left, this method is called with all
+ * three arguments (\em link, \em x, \em y) set to -1.
+ *
+ * When coordinates are supported, a change of the coordinates also
+ * causes emitting this signal.
+ */
+ virtual bool enter (Widget *widget, int link, int img, int x, int y);
+
+ /**
+ * \brief Called, when the user has pressed the mouse button on a
+ * link (but not yet released).
+ *
+ * The causing event is passed as \em event.
+ */
+ virtual bool press (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+
+ /**
+ * \brief Called, when the user has released the mouse button on a
+ * link.
+ *
+ * The causing event is passed as \em event.
+ */
+ virtual bool release (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+
+ /**
+ * \brief Called, when the user has clicked on a link.
+ *
+ * For mouse interaction, this is equivalent to "press" and "release"
+ * on the same link. In this case, \em event contains the "release"
+ * event.
+ *
+ *
+ * When activating links via keyboard is supported, only a "clicked"
+ * signal will be emitted, and \em event will be NULL.
+ */
+ virtual bool click (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ };
+
+ class LinkEmitter: public lout::signal::Emitter
+ {
+ private:
+ enum { ENTER, PRESS, RELEASE, CLICK };
+
+ protected:
+ bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
+ int argc, lout::object::Object **argv);
+
+ public:
+ inline void connectLink (LinkReceiver *receiver) { connect (receiver); }
+
+ bool emitEnter (Widget *widget, int link, int img, int x, int y);
+ bool emitPress (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool emitRelease (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool emitClick (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ };
+
+ LinkEmitter linkEmitter;
+
private:
class Emitter: public lout::signal::Emitter
{
@@ -37,7 +109,7 @@ private:
protected:
bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
- int argc, Object **argv);
+ int argc, lout::object::Object **argv);
public:
inline void connectLayout (Receiver *receiver) { connect (receiver); }
@@ -47,7 +119,7 @@ private:
Emitter emitter;
- class Anchor: public object::Object
+ class Anchor: public lout::object::Object
{
public:
char *name;
@@ -58,10 +130,10 @@ private:
};
Platform *platform;
- container::typed::List <View> *views;
+ View *view;
Widget *topLevel, *widgetAtPoint;
- /* The state, which must be projected into the views. */
+ /* The state, which must be projected into the view. */
style::Color *bgColor;
style::Cursor cursor;
int canvasWidth, canvasAscent, canvasDescent;
@@ -70,7 +142,7 @@ private:
int scrollX, scrollY, viewportWidth, viewportHeight;
bool canvasHeightGreater;
int hScrollbarThickness, vScrollbarThickness;
-
+
HPosition scrollTargetHpos;
VPosition scrollTargetVpos;
int scrollTargetX, scrollTargetY, scrollTargetWidth, scrollTargetHeight;
@@ -80,7 +152,7 @@ private:
bool scrollIdleNotInterrupted;
/* Anchors of the widget tree */
- container::typed::HashTable <object::String, Anchor> *anchorsTable;
+ lout::container::typed::HashTable <lout::object::String, Anchor> *anchorsTable;
SelectionState selectionState;
FindtextState findtextState;
@@ -136,13 +208,31 @@ private:
int ex, int ey, int ewidth, int eheight);
void queueResize ();
void removeWidget ();
-
+
public:
Layout (Platform *platform);
~Layout ();
- misc::ZoneAllocator *textZone;
-
+ inline void connectLink (LinkReceiver *receiver)
+ { linkEmitter.connectLink (receiver); }
+
+ inline bool emitLinkEnter (Widget *w, int link, int img, int x, int y)
+ { return linkEmitter.emitEnter (w, link, img, x, y); }
+
+ inline bool emitLinkPress (Widget *w, int link, int img,
+ int x, int y, EventButton *event)
+ { return linkEmitter.emitPress (w, link, img, x, y, event); }
+
+ inline bool emitLinkRelease (Widget *w, int link, int img,
+ int x, int y, EventButton *event)
+ { return linkEmitter.emitRelease (w, link, img, x, y, event); }
+
+ inline bool emitLinkClick (Widget *w, int link, int img,
+ int x, int y, EventButton *event)
+ { return linkEmitter.emitClick (w, link, img, x, y, event); }
+
+ lout::misc::ZoneAllocator *textZone;
+
void addWidget (Widget *widget);
void setWidget (Widget *widget);
@@ -159,6 +249,7 @@ public:
void scrollTo (HPosition hpos, VPosition vpos,
int x, int y, int width, int height);
+ void scroll (ScrollCommand);
void setAnchor (const char *anchor);
/* View */
@@ -168,7 +259,7 @@ public:
/**
* \brief This function is called by a view, to delegate a button press
* event.
- *
+ *
* \em numPressed is 1 for simple presses, 2 for double presses etc. (more
* that 2 is never needed), \em x and \em y the world coordinates, and
* \em button the number of the button pressed.
@@ -216,19 +307,34 @@ public:
return platform->prevGlyph (text, idx);
}
+ inline float dpiX ()
+ {
+ return platform->dpiX ();
+ }
+
+ inline float dpiY ()
+ {
+ return platform->dpiY ();
+ }
+
inline style::Font *createFont (style::FontAttrs *attrs, bool tryEverything)
{
return platform->createFont (attrs, tryEverything);
}
- inline style::Color *createSimpleColor (int color)
+ inline bool fontExists (const char *name)
+ {
+ return platform->fontExists (name);
+ }
+
+ inline style::Color *createColor (int color)
{
- return platform->createSimpleColor (color);
+ return platform->createColor (color);
}
- inline style::Color *createShadedColor (int color)
+ inline style::Tooltip *createTooltip (const char *text)
{
- return platform->createShadedColor (color);
+ return platform->createTooltip (text);
}
inline Imgbuf *createImgbuf (Imgbuf::Type type, int width, int height)
@@ -250,8 +356,9 @@ public:
emitter.connectLayout (receiver); }
/** \brief See dw::core::FindtextState::search. */
- inline FindtextState::Result search (const char *str, bool caseSens)
- { return findtextState.search (str, caseSens); }
+ inline FindtextState::Result search (const char *str, bool caseSens,
+ int backwards)
+ { return findtextState.search (str, caseSens, backwards); }
/** \brief See dw::core::FindtextState::resetSearch. */
inline void resetSearch () { findtextState.resetSearch (); }
diff --git a/dw/listitem.cc b/dw/listitem.cc
index ba960b46..ed7a2c75 100644
--- a/dw/listitem.cc
+++ b/dw/listitem.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -41,16 +40,20 @@ ListItem::~ListItem()
void ListItem::initWithWidget (core::Widget *widget,
core::style::Style *style)
{
+ hasListitemValue = true;
addWidget (widget, style);
addSpace (style);
- updateValue ();
+ if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
+ updateValue ();
}
-void ListItem::initWithText (char *text, core::style::Style *style)
+void ListItem::initWithText (const char *text, core::style::Style *style)
{
+ hasListitemValue = true;
addText (text, style);
- addSpace (style);
- updateValue ();
+ addSpace (style);
+ if (style->listStylePosition == core::style::LIST_STYLE_POSITION_OUTSIDE)
+ updateValue ();
}
int ListItem::getValue ()
@@ -58,7 +61,7 @@ int ListItem::getValue ()
if (words->size () == 0)
return 0;
else
- return words->get(0).size.width + words->get(0).origSpace;
+ return words->getRef(0)->size.width + words->getRef(0)->origSpace;
}
void ListItem::setMaxValue (int maxValue, int value)
diff --git a/dw/listitem.hh b/dw/listitem.hh
index ea24af3e..2e303d5d 100644
--- a/dw/listitem.hh
+++ b/dw/listitem.hh
@@ -19,7 +19,7 @@ public:
~ListItem();
void initWithWidget (core::Widget *widget, core::style::Style *style);
- void initWithText (char *texty, core::style::Style *style);
+ void initWithText (const char *text, core::style::Style *style);
};
} // namespace dw
diff --git a/dw/platform.hh b/dw/platform.hh
index 0ae5d508..69d1feab 100644
--- a/dw/platform.hh
+++ b/dw/platform.hh
@@ -13,7 +13,7 @@ namespace core {
*
* \sa\ref dw-overview
*/
-class Platform: public object::Object
+class Platform: public lout::object::Object
{
public:
/*
@@ -27,7 +27,7 @@ public:
* a layout.
*/
virtual void setLayout (Layout *layout) = 0;
-
+
/*
* -------------------------
* Operations on views
@@ -45,10 +45,10 @@ public:
* from the related layout.
*/
virtual void detachView (View *view) = 0;
-
+
/*
* -----------------------------------
- * Platform dependant properties
+ * Platform dependent properties
* -----------------------------------
*/
@@ -66,7 +66,17 @@ public:
* \brief Return the index of the previous glyph in string text.
*/
virtual int prevGlyph (const char *text, int idx) = 0;
-
+
+ /**
+ * \brief Return screen resolution in x-direction.
+ */
+ virtual float dpiX () = 0;
+
+ /**
+ * \brief Return screen resolution in y-direction.
+ */
+ virtual float dpiY () = 0;
+
/*
* ---------------------------------------------------------
* These are to encapsulate some platform dependencies
@@ -75,7 +85,7 @@ public:
/**
* \brief Add an idle function.
- *
+ *
* An idle function is called once, when no other
* tasks are to be done (e.g. there are no events to process), and then
* removed from the queue. The return value is a number, which can be
@@ -95,10 +105,10 @@ public:
*/
/**
- * \brief Create a (platform dependant) font.
+ * \brief Create a (platform dependent) font.
*
* Typically, within a platform, a sub class of dw::core::style::Font
- * is defined, which holds more platform dependant data.
+ * is defined, which holds more platform dependent data.
*
* Also, this method must fill the attributes "font" (when needed),
* "ascent", "descent", "spaceSidth" and "xHeight". If "tryEverything"
@@ -108,17 +118,18 @@ public:
*/
virtual style::Font *createFont (style::FontAttrs *attrs,
bool tryEverything) = 0;
-
+
+ virtual bool fontExists (const char *name) = 0;
+
/**
- * \brief Create a simple color resource for a given 0xrrggbb value.
+ * \brief Create a color resource for a given 0xrrggbb value.
*/
- virtual style::Color *createSimpleColor (int color) = 0;
+ virtual style::Color *createColor (int color) = 0;
/**
- * \brief Create a shaded color resource for a given 0xrrggbb value.
+ * \brief Create a tooltip
*/
- virtual style::Color *createShadedColor (int color) = 0;
-
+ virtual style::Tooltip *createTooltip (const char *text) = 0;
/*
* --------------------
diff --git a/dw/ruler.cc b/dw/ruler.cc
index abefa1bf..6dce07d0 100644
--- a/dw/ruler.cc
+++ b/dw/ruler.cc
@@ -14,15 +14,14 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ruler.hh"
#include "../lout/misc.hh"
-
+
#include <stdio.h>
namespace dw {
diff --git a/dw/ruler.hh b/dw/ruler.hh
index a1ae67ea..32e859a1 100644
--- a/dw/ruler.hh
+++ b/dw/ruler.hh
@@ -17,12 +17,12 @@ class Ruler: public core::Widget
{
protected:
void sizeRequestImpl (core::Requisition *requisition);
- void draw (core::View *view, core::Rectangle *area);
+ void draw (core::View *view, core::Rectangle *area);
public:
Ruler ();
- core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
};
} // namespace dw
diff --git a/dw/selection.cc b/dw/selection.cc
index 3153576f..275eddaa 100644
--- a/dw/selection.cc
+++ b/dw/selection.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -24,6 +23,8 @@
#include <string.h>
+using namespace lout;
+
/*
* strndup() is a GNU extension.
*/
@@ -95,13 +96,13 @@ void SelectionState::resetLink ()
link = NULL;
linkState = LINK_NONE;
}
-
+
bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
EventButton *event, bool withinContent)
{
Widget *itWidget = it->getWidget ();
bool ret = false;
-
+
if (event && event->button == 1 &&
!withinContent && event->numPressed == 2) {
// When the user double-clicks on empty parts, emit the double click
@@ -117,8 +118,7 @@ bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
if (linkNo != -1) {
// link handling
if (event) {
- // return value is ignored
- itWidget->emitLinkPress (linkNo, -1, -1, -1, event);
+ (void) layout->emitLinkPress (itWidget, linkNo, -1, -1, -1, event);
resetLink ();
linkState = LINK_PRESSED;
linkButton = event->button;
@@ -161,7 +161,7 @@ bool SelectionState::buttonPress (Iterator *it, int charPos, int linkNo,
} else {
if (event && event->button == 3) {
// menu popup
- itWidget->emitLinkPress (-1, -1, -1, -1, event);
+ layout->emitLinkPress (itWidget, -1, -1, -1, -1, event);
ret = true;
}
}
@@ -181,14 +181,12 @@ bool SelectionState::buttonRelease (Iterator *it, int charPos, int linkNo,
// link handling
ret = true;
if (linkNo != -1)
- // return value is ignored
- itWidget->emitLinkRelease (linkNo, -1, -1, -1, event);
+ (void) layout->emitLinkRelease (itWidget, linkNo, -1, -1, -1, event);
// The link where the user clicked the mouse button?
if (linkNo == linkNumber) {
resetLink ();
- // return value is ignored
- itWidget->emitLinkClick (linkNo, -1, -1, -1, event);
+ (void) layout->emitLinkClick (itWidget, linkNo, -1, -1, -1, event);
} else {
if (event->button == 1)
// Reset links and switch to selection mode. The selection
diff --git a/dw/selection.hh b/dw/selection.hh
index 9cc8d25f..ba03fd18 100644
--- a/dw/selection.hh
+++ b/dw/selection.hh
@@ -8,30 +8,28 @@
namespace dw {
namespace core {
-using namespace lout;
-
/**
* \brief This class handles selections, as well as activation of links,
* which is closely related.
*
* <h3>General Overview</h3>
- *
+ *
* dw::core::SelectionState is associated with dw::core::Layout. The selection
* state is controlled by "abstract events", which are sent by single
* widgets by calling one of the following methods:
- *
+ *
* <ul>
* <li> dw::core::SelectionState::buttonPress for button press events,
* <li> dw::core::SelectionState::buttonRelease for button release events, and
* <li> dw::core::SelectionState::buttonMotion for motion events (with pressed
* mouse button).
* </ul>
- *
+ *
* The widget must construct simple iterators (dw::core::Iterator), which will
* be transferred to deep iterators (dw::core::DeepIterator), see below for
* more details. All event handling methods have the same signature, the
* arguments in detail are:
- *
+ *
* <table>
* <tr><td>dw::core::Iterator *it <td>the iterator pointing on the item
* under the mouse pointer; this
@@ -42,7 +40,7 @@ using namespace lout;
* within the iterator,
* <tr><td>int linkNo <td>if this item is associated with a
* link, its number (see
- * dw::core::Widget::LinkReceiver),
+ * dw::core::Layout::LinkReceiver),
* otherwise -1
* <tr><td>dw::core::EventButton *event <td>the event itself; only the button
* is used
@@ -64,26 +62,26 @@ using namespace lout;
* simple iterator is discarded and instead the stack has an iterator
* pointing to text at the top. As a result, only the first letter of the
* ALT text would be copied.
- *
+ *
* To avoid this problem, widgets should in this case pass
* dw::core::SelectionState::END_OF_WORD as \em charPos, which is then
* automatically reduced to the actual length of the deep(!) iterator.
- *
+ *
* The return value is the same as in DwWidget event handling methods.
* I.e., in most cases, they should simply return it. The events
- * dw::core::Widget::LinkReceiver::press,
- * dw::core::Widget::LinkReceiver::release and
- * dw::core::Widget::LinkReceiver::click (but not
- * dw::core::Widget::LinkReceiver::enter) are emitted by these methods, so
+ * dw::core::Layout::LinkReceiver::press,
+ * dw::core::Layout::LinkReceiver::release and
+ * dw::core::Layout::LinkReceiver::click (but not
+ * dw::core::Layout::LinkReceiver::enter) are emitted by these methods, so
* that widgets which let dw::core::SelectionState handle links, should only
- * emit dw::core::Widget::LinkReceiver::enter for themselves.
- *
+ * emit dw::core::Layout::LinkReceiver::enter for themselves.
+ *
* <h3>Selection State</h3>
- *
+ *
* Selection interferes with handling the activation of links, so the
* latter is also handled by the dw::core::SelectionState. Details are based on
* following guidelines:
- *
+ *
* <ol>
* <li> It should be simple to select links and to start selection in
* links. The rule to distinguish between link activation and
@@ -91,18 +89,18 @@ using namespace lout;
* the link. (This is, IMO, a useful feature. Even after drag and
* drop has been implemented in dillo, this should be somehow
* preserved.)
- *
+ *
* <li> The selection should stay as long as possible, i.e., the old
* selection is only cleared when a new selection is started.
* </ol>
- *
+ *
* The latter leads to a model with two states: the selection state and
* the link handling state.
- *
+ *
* The general selection works, for events not pointing on links, like
* this (numbers in parantheses after the event denote the button, "n"
* means arbitrary button):
- *
+ *
* \dot
* digraph G {
* node [shape=ellipse, fontname=Helvetica, fontsize=10];
@@ -122,15 +120,15 @@ using namespace lout;
* q -> SELECTED [label="yes"];
* q -> NONE [label="no"];
* SELECTED -> SELECTING [label="press(1)"];
- *
+ *
* }
* \enddot
*
* The selected region is represented by two instances of
* dw::core::DeepIterator.
- *
+ *
* Links are handled by a different state machine:
- *
+ *
* \dot
* digraph G {
* node [shape=ellipse, fontname=Helvetica, fontsize=10];
@@ -170,16 +168,16 @@ using namespace lout;
* eventually be SELECTED/SELECTING, with the original and the current
* position making up the selection region. This happens for button 1,
* events with buttons other than 1 do not affect selection at all.
- *
- *
+ *
+ *
* \todo dw::core::SelectionState::buttonMotion currently always assumes
* that button 1 has been pressed (since otherwise it would not do
* anything). This should be made a bit cleaner.
- *
+ *
* \todo The selection should be cleared, when the user selects something
* somewhere else (perhaps switched into "non-active" mode, as e.g. Gtk+
* does).
- *
+ *
*/
class SelectionState
{
@@ -248,15 +246,15 @@ private:
public:
enum EventType { BUTTON_PRESS, BUTTON_RELEASE, BUTTON_MOTION };
-
+
SelectionState ();
~SelectionState ();
-
+
inline void setLayout (Layout *layout) { this->layout = layout; }
void reset ();
inline void connectDoubleClick (DoubleClickReceiver *receiver)
{ doubleClickEmitter.connectDoubleClick (receiver); }
-
+
bool buttonPress (Iterator *it, int charPos, int linkNo,
EventButton *event, bool withinContent);
bool buttonRelease (Iterator *it, int charPos, int linkNo,
diff --git a/dw/style.cc b/dw/style.cc
index 207c47a3..b361e068 100644
--- a/dw/style.cc
+++ b/dw/style.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -24,6 +23,9 @@
#include <ctype.h>
#include "core.hh"
+#include "../lout/msg.h"
+
+using namespace lout;
namespace dw {
namespace core {
@@ -37,14 +39,13 @@ void StyleAttrs::initValues ()
textDecoration = TEXT_DECORATION_NONE;
textAlign = TEXT_ALIGN_LEFT;
textAlignChar = '.';
+ listStylePosition = LIST_STYLE_POSITION_OUTSIDE;
listStyleType = LIST_STYLE_TYPE_DISC;
- valign = VALIGN_MIDDLE;
+ valign = VALIGN_BASELINE;
backgroundColor = NULL;
- width = LENGTH_AUTO;
- height = LENGTH_AUTO;
+ width = height = lineHeight = LENGTH_AUTO;
vloat = FLOAT_NONE;
clear = CLEAR_NONE;
-
margin.setVal (0);
borderWidth.setVal (0);
padding.setVal (0);
@@ -52,6 +53,7 @@ void StyleAttrs::initValues ()
setBorderStyle (BORDER_NONE);
hBorderSpacing = 0;
vBorderSpacing = 0;
+ wordSpacing = 0;
display = DISPLAY_INLINE;
whiteSpace = WHITE_SPACE_NORMAL;
@@ -64,12 +66,9 @@ void StyleAttrs::initValues ()
*/
void StyleAttrs::resetValues ()
{
- x_link = -1;
x_img = -1;
- x_tooltip = NULL;
- textAlign = TEXT_ALIGN_LEFT; /* ??? */
- valign = VALIGN_MIDDLE;
+ valign = VALIGN_BASELINE;
textAlignChar = '.';
vloat = FLOAT_NONE; /** \todo Correct? Check specification. */
clear = CLEAR_NONE; /** \todo Correct? Check specification. */
@@ -86,8 +85,6 @@ void StyleAttrs::resetValues ()
vBorderSpacing = 0;
display = DISPLAY_INLINE;
- whiteSpace = WHITE_SPACE_NORMAL;
- cursor = CURSOR_DEFAULT; /** \todo Check CSS specification again. */
}
/**
@@ -121,8 +118,10 @@ bool StyleAttrs::equals (object::Object *other) {
textAlignChar == otherAttrs->textAlignChar &&
hBorderSpacing == otherAttrs->hBorderSpacing &&
vBorderSpacing == otherAttrs->vBorderSpacing &&
+ wordSpacing == otherAttrs->wordSpacing &&
width == otherAttrs->width &&
height == otherAttrs->height &&
+ lineHeight == otherAttrs->lineHeight &&
margin.equals (&otherAttrs->margin) &&
borderWidth.equals (&otherAttrs->borderWidth) &&
padding.equals (&otherAttrs->padding) &&
@@ -136,7 +135,9 @@ bool StyleAttrs::equals (object::Object *other) {
borderStyle.left == otherAttrs->borderStyle.left &&
display == otherAttrs->display &&
whiteSpace == otherAttrs->whiteSpace &&
+ listStylePosition == otherAttrs->listStylePosition &&
listStyleType == otherAttrs->listStyleType &&
+ cursor == otherAttrs->cursor &&
x_link == otherAttrs->x_link &&
x_img == otherAttrs->x_img &&
x_tooltip == otherAttrs->x_tooltip);
@@ -152,10 +153,12 @@ int StyleAttrs::hashValue () {
textAlignChar +
hBorderSpacing +
vBorderSpacing +
+ wordSpacing +
width +
height +
+ lineHeight +
margin.hashValue () +
- borderWidth.hashValue () +
+ borderWidth.hashValue () +
padding.hashValue () +
(intptr_t) borderColor.top +
(intptr_t) borderColor.right +
@@ -167,7 +170,9 @@ int StyleAttrs::hashValue () {
borderStyle.left +
display +
whiteSpace +
+ listStylePosition +
listStyleType +
+ cursor +
x_link +
x_img +
(intptr_t) x_tooltip;
@@ -222,7 +227,7 @@ Style::~Style ()
x_tooltip->unref();
styleTable->remove (this);
- totalRef--;
+ totalRef--;
}
void Style::copyAttrs (StyleAttrs *attrs)
@@ -238,8 +243,10 @@ void Style::copyAttrs (StyleAttrs *attrs)
clear = attrs->clear;
hBorderSpacing = attrs->hBorderSpacing;
vBorderSpacing = attrs->vBorderSpacing;
+ wordSpacing = attrs->wordSpacing;
width = attrs->width;
height = attrs->height;
+ lineHeight = attrs->lineHeight;
margin = attrs->margin;
borderWidth = attrs->borderWidth;
padding = attrs->padding;
@@ -247,6 +254,7 @@ void Style::copyAttrs (StyleAttrs *attrs)
borderStyle = attrs->borderStyle;
display = attrs->display;
whiteSpace = attrs->whiteSpace;
+ listStylePosition = attrs->listStylePosition;
listStyleType = attrs->listStyleType;
cursor = attrs->cursor;
x_link = attrs->x_link;
@@ -261,8 +269,11 @@ bool FontAttrs::equals(object::Object *other)
FontAttrs *otherAttrs = (FontAttrs*)other;
return
this == otherAttrs ||
- (size == otherAttrs->size && weight == otherAttrs->weight &&
- style == otherAttrs->style && strcmp (name, otherAttrs->name) == 0);
+ (size == otherAttrs->size &&
+ weight == otherAttrs->weight &&
+ style == otherAttrs->style &&
+ letterSpacing == otherAttrs->letterSpacing &&
+ strcmp (name, otherAttrs->name) == 0);
}
int FontAttrs::hashValue()
@@ -271,12 +282,13 @@ int FontAttrs::hashValue()
h = (h << 5) - h + size;
h = (h << 5) - h + weight;
h = (h << 5) - h + style;
+ h = (h << 5) - h + letterSpacing;
return h;
}
Font::~Font ()
{
- delete name;
+ free ((char*)name);
}
void Font::copyAttrs (FontAttrs *attrs)
@@ -285,6 +297,7 @@ void Font::copyAttrs (FontAttrs *attrs)
size = attrs->size;
weight = attrs->weight;
style = attrs->style;
+ letterSpacing = attrs->letterSpacing;
}
Font *Font::create0 (Layout *layout, FontAttrs *attrs,
@@ -298,43 +311,9 @@ Font *Font::create (Layout *layout, FontAttrs *attrs)
return create0 (layout, attrs, false);
}
-Font *Font::createFromList (Layout *layout, FontAttrs *attrs,
- char *defaultFamily)
+bool Font::exists (Layout *layout, const char *name)
{
- Font *font = NULL;
- FontAttrs attrs2;
- char *comma, *list, *current;
-
- attrs2 = *attrs;
- current = list = strdup (attrs->name);
-
- while (current && (font == NULL)) {
- comma = strchr (current, ',');
- if (comma) *comma = 0;
-
- attrs2.name = current;
- font = create0 (layout, &attrs2, false);
- if (font)
- break;
-
- if (comma) {
- current = comma + 1;
- while (isspace (*current)) current++;
- } else
- current = NULL;
- }
-
- delete list;
-
- if (font == NULL) {
- attrs2.name = defaultFamily;
- font = create0 (layout, &attrs2, true);
- }
-
- if (font == NULL)
- fprintf (stderr, "Could not find any font.\n");
-
- return font;
+ return layout->fontExists (name);
}
// ----------------------------------------------------------------------
@@ -342,12 +321,12 @@ Font *Font::createFromList (Layout *layout, FontAttrs *attrs,
bool ColorAttrs::equals(object::Object *other)
{
ColorAttrs *oc = (ColorAttrs*)other;
- return this == oc || (color == oc->color && type == oc->type);
+ return this == oc || (color == oc->color);
}
int ColorAttrs::hashValue()
{
- return color ^ type;
+ return color;
}
Color::~Color ()
@@ -410,72 +389,33 @@ int Color::shadeColor (int color, Shading shading)
}
}
-
-Color *Color::create (Layout *layout, int col, Type type)
+
+Color *Color::create (Layout *layout, int col)
{
- ColorAttrs attrs(col, type);
- Color *color = NULL;
-
- switch (type) {
- case TYPE_SIMPLE:
- color = layout->createSimpleColor (col);
- break;
- case TYPE_SHADED:
- color = layout->createShadedColor (col);
- break;
- }
+ ColorAttrs attrs(col);
- return color;
+ return layout->createColor (col);
+}
+
+Tooltip *Tooltip::create (Layout *layout, const char *text)
+{
+ return layout->createTooltip (text);
}
// ----------------------------------------------------------------------
-/**
- * \brief Draw a part of a border.
- */
-static void drawPolygon (View *view, Color *color, Color::Shading shading,
- int x1, int y1, int x2, int y2,
- int width, int w1, int w2)
-{
- int points[4][2];
-
- if (width != 0) {
- if (width == 1) {
- if (x1 == x2)
- view->drawLine (color, shading, x1, y1, x2, y2 - 1);
- else
- view->drawLine (color, shading, x1, y1, x2 - 1, y2);
- } else if (width == -1) {
- if (x1 == x2)
- view->drawLine (color, shading, x1 - 1, y1, x2 - 1, y2 - 1);
- else
- view->drawLine (color, shading, x1, y1 - 1, x2 - 1, y2 - 1);
- } else {
- points[0][0] = x1;
- points[0][1] = y1;
- points[1][0] = x2;
- points[1][1] = y2;
-
- if (x1 == x2) {
- points[2][0] = x1 + width;
- points[2][1] = y2 + w2;
- points[3][0] = x1 + width;
- points[3][1] = y1 + w1;
- } else {
- points[2][0] = x2 + w2;
- points[2][1] = y1 + width;
- points[3][0] = x1 + w1;
- points[3][1] = y1 + width;
- }
-
- /*
- printf ("drawPolygon: (%d, %d) .. (%d, %d) .. (%d, %d) .. (%d, %d)\n",
- points[0][0], points[0][1], points[1][0], points[1][1],
- points[2][0], points[2][1], points[3][0], points[3][1]);
- */
- view->drawPolygon (color, shading, true, points, 4);
- }
- }
+static void drawTriangle (View *view, Color *color, Color::Shading shading,
+ int x1, int y1, int x2, int y2, int x3, int y3) {
+ int points[3][2];
+
+ points[0][0] = x1;
+ points[0][1] = y1;
+ points[1][0] = x2;
+ points[1][1] = y2;
+ points[2][0] = x3;
+ points[2][1] = y3;
+
+ view->drawPolygon (color, shading, true, points, 3);
}
/**
@@ -492,18 +432,17 @@ void drawBorder (View *view, Rectangle *area,
Color::Shading top, right, bottom, left;
int xb1, yb1, xb2, yb2, xp1, yp1, xp2, yp2;
- if (style->borderStyle.top == BORDER_NONE)
- return;
-
+ // top left and bottom right point of outer border boundary
xb1 = x + style->margin.left;
yb1 = y + style->margin.top;
- xb2 = xb1 + width - style->margin.left - style->margin.right;
- yb2 = yb1 + height - style->margin.top - style->margin.bottom;
+ xb2 = x + width - style->margin.right;
+ yb2 = y + height - style->margin.bottom;
- xp1 = xb1 + style->borderWidth.top;
- yp1 = yb1 + style->borderWidth.left;
- xp2 = xb2 + style->borderWidth.bottom;
- yp2 = yb2 + style->borderWidth.right;
+ // top left and bottom right point of inner border boundary
+ xp1 = xb1 + style->borderWidth.left;
+ yp1 = yb1 + style->borderWidth.top;
+ xp2 = xb2 - style->borderWidth.right;
+ yp2 = yb2 - style->borderWidth.bottom;
light = inverse ? Color::SHADING_DARK : Color::SHADING_LIGHT;
dark = inverse ? Color::SHADING_LIGHT : Color::SHADING_DARK;
@@ -525,18 +464,47 @@ void drawBorder (View *view, Rectangle *area,
break;
}
- drawPolygon (view, style->borderColor.top, top, xb1, yb1, xb2, yb1,
- style->borderWidth.top, style->borderWidth.left,
- - style->borderWidth.right);
- drawPolygon (view, style->borderColor.right, right, xb2, yb1, xb2, yb2,
- - style->borderWidth.right, style->borderWidth.top,
- - style->borderWidth.bottom);
- drawPolygon (view, style->borderColor.bottom, bottom, xb1, yb2, xb2, yb2,
- - style->borderWidth.bottom, style->borderWidth.left,
- - style->borderWidth.right);
- drawPolygon (view, style->borderColor.left, left, xb1, yb1, xb1, yb2,
- style->borderWidth.left, style->borderWidth.top,
- - style->borderWidth.bottom);
+ if (style->borderStyle.top != BORDER_NONE && style->borderColor.top)
+ view->drawRectangle(style->borderColor.top, top, true,
+ xb1, yb1, xb2 - xb1, style->borderWidth.top);
+
+ if (style->borderStyle.bottom != BORDER_NONE && style->borderColor.bottom)
+ view->drawRectangle(style->borderColor.bottom, bottom, true,
+ xb1, yb2, xb2 - xb1, - style->borderWidth.bottom);
+
+ if (style->borderStyle.left != BORDER_NONE && style->borderColor.left)
+ view->drawRectangle(style->borderColor.left, left, true,
+ xb1, yp1, style->borderWidth.left, yp2 - yp1);
+
+ if (style->borderWidth.left > 1) {
+ if (style->borderWidth.top > 1 &&
+ (style->borderColor.left != style->borderColor.top ||
+ left != top))
+ drawTriangle (view, style->borderColor.left, left,
+ xb1, yp1, xp1, yp1, xb1, yb1);
+ if (style->borderWidth.bottom > 1 &&
+ (style->borderColor.left != style->borderColor.bottom ||
+ left != bottom))
+ drawTriangle (view, style->borderColor.left, left,
+ xb1, yp2, xp1, yp2, xb1, yb2);
+ }
+
+ if (style->borderStyle.right != BORDER_NONE && style->borderColor.right)
+ view->drawRectangle(style->borderColor.right, right, true,
+ xb2, yp1, - style->borderWidth.right, yp2 - yp1);
+
+ if (style->borderWidth.right > 1) {
+ if (style->borderWidth.top > 1 &&
+ (style->borderColor.right != style->borderColor.top ||
+ right != top))
+ drawTriangle (view, style->borderColor.right, right,
+ xb2, yp1, xp2, yp1, xb2, yb1);
+ if (style->borderWidth.bottom > 1 &&
+ (style->borderColor.right != style->borderColor.bottom ||
+ right != bottom))
+ drawTriangle (view, style->borderColor.right, right,
+ xb2, yp2, xp2, yp2, xb2, yb2);
+ }
}
@@ -574,12 +542,12 @@ void drawBackground (View *view, Rectangle *area,
// ----------------------------------------------------------------------
static const char
- *roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" },
- *roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" },
- *roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" },
- *roman_I3[] = { "","M","MM","MMM","MMMM" };
+ *const roman_I0[] = { "","I","II","III","IV","V","VI","VII","VIII","IX" },
+ *const roman_I1[] = { "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC" },
+ *const roman_I2[] = { "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM" },
+ *const roman_I3[] = { "","M","MM","MMM","MMMM" };
-void strtolower (char *s)
+static void strtolower (char *s)
{
for ( ; *s; s++)
*s = tolower (*s);
@@ -596,16 +564,21 @@ void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType)
bool low = false;
int start_ch = 'A';
+ if (buflen <= 0)
+ return;
+
switch(listStyleType){
case LIST_STYLE_TYPE_LOWER_ALPHA:
+ case LIST_STYLE_TYPE_LOWER_LATIN:
start_ch = 'a';
case LIST_STYLE_TYPE_UPPER_ALPHA:
+ case LIST_STYLE_TYPE_UPPER_LATIN:
i0 = num - 1;
i1 = i0/26 - 1; i2 = i1/26 - 1;
if (i2 > 25) /* more than 26+26^2+26^3=18278 elements ? */
- sprintf(buf, "****.");
+ snprintf(buf, buflen, "****.");
else
- sprintf(buf, "%c%c%c.",
+ snprintf(buf, buflen, "%c%c%c.",
i2<0 ? ' ' : start_ch + i2%26,
i1<0 ? ' ' : start_ch + i1%26,
i0<0 ? ' ' : start_ch + i0%26);
@@ -617,18 +590,23 @@ void numtostr (int num, char *buf, int buflen, ListStyleType listStyleType)
i1 = i0/10; i2 = i1/10; i3 = i2/10;
i0 %= 10; i1 %= 10; i2 %= 10;
if (num < 0 || i3 > 4) /* more than 4999 elements ? */
- sprintf(buf, "****.");
+ snprintf(buf, buflen, "****.");
else
snprintf(buf, buflen, "%s%s%s%s.", roman_I3[i3], roman_I2[i2],
roman_I1[i1], roman_I0[i0]);
- if (low)
- strtolower(buf);
break;
case LIST_STYLE_TYPE_DECIMAL:
default:
- sprintf(buf, "%d.", num);
+ snprintf(buf, buflen, "%d.", num);
break;
}
+
+ // ensure termination
+ buf[buflen - 1] = '\0';
+
+ if (low)
+ strtolower(buf);
+
}
} // namespace style
diff --git a/dw/style.hh b/dw/style.hh
index a96a3471..81d47930 100644
--- a/dw/style.hh
+++ b/dw/style.hh
@@ -14,100 +14,100 @@ namespace core {
* \brief Anything related to Dillo %Widget styles is defined here.
*
* <h3>Overview</h3>
- *
+ *
* dw::core::style::Style provides some resources and attributes for
* drawing widgets, as well as for parts of a widget (e.g., dw::Textblock
* uses styles for its words). Creating a style is done by filling a
* dw::core::style::StyleAttrs with the attributes and calling
* dw::core::style::Style::create:
- *
+ *
* \code
* dw::core::style::Style styleAttrs;
* dw::core::style::Style *style;
* dw::core::Layout *layout;
- *
+ *
* // ...
- *
+ *
* styleAttrs.foo = bar;
* // etc.
* style = dw::core::style::Style::create (&styleAttrs, layout);
* // do something with style
* \endcode
- *
+ *
* After this, the attributes of a dw::core::style::Style should not be
* changed anymore, since styles are often shared between different
* widgets etc. (see below). Most times, you simply copy the attributes
* of another style (possible, since dw::core::style::Style is a sub
* class of dw::core::style::StyleAttrs), modify them and create a new
* style:
- *
+ *
* \code
* styleAttrs = *anotherStyle;
* styleAttrs.foo = baz;
* style = dw::core::style::Style::create (&styleAttrs, layout);
* \endcode
- *
+ *
* The dw::core::style::Font structure can be created by
* dw::core::style::Font::create, in a similar, with
* dw::core::style::FontAttrs, and colors by
* dw::core::style::Color::create, passing 0xrrggbb as an
* argument. Furthermore, there is dw::core::style::Tooltip, created by
* dw::core::style::Tooltip::create.
- *
+ *
* Notice that fonts, colors and tooltips are only intended to be used in
* conjunction with dw::core::style::Style.
- *
- *
+ *
+ *
* <h3>Naming</h3>
- *
+ *
* dw::core::style::Style will become important for CSS, each CSS
* attribute, which is supported by dillo, will refer to an attribute in
* dw::core::style::Style. For this reason, the attributes in
* dw::core::style::Style get the names from the CSS attributes, with
* "camelCase" instead of hythens (e.g. "background-color" becomes
* "backgroundColor").
- *
+ *
* However, dw::core::style::Style will be extended by some more
* attributes, which are not defined by CSS. To distinguish them, they
* get the prefix "x_", e.g. dw::core::style::Style::x_link.
- *
- *
+ *
+ *
* <h3>Lengths and Percentages</h3>
- *
+ *
* dw::core::style::Length is a simple data type for lengths and
* percentages:
- *
+ *
* <ul>
* <li> A length refers to an absolute measurement. It is used to
* represent the HTML type %Pixels; and the CSS type \<length\>.
- *
- * For CSS lenghts, there are two units: (i) pixels and absolute
+ *
+ * For CSS lengths, there are two units: (i) pixels and absolute
* units, which have to be converted to pixels (a pixel is, unlike
* in the CSS specification, treated as absolute unit), and (ii) the
* relative units "em" and "ex" (see below).
- *
+ *
* <li> A percentage refers to a value relative to another value. It is
* used for the HTML type %Length; (except %Pixels;), and the CSS
* type \<percentage\>.
- *
+ *
* <li> A relative length can be used in lists of HTML MultiLengths.
* </ul>
- *
+ *
* Since many values in CSS may be either lengths or percentages, a
* single type is very useful.
- *
+ *
* <h4>Useful Functions</h4>
- *
+ *
* Creating lengths:
- *
+ *
* <ul>
* <li> dw::core::style::createAbsLength
* <li> dw::core::style::createPerLength
* <li> dw::core::style::createRelLength
* </ul>
- *
+ *
* Examine lengths:
- *
+ *
* <ul>
* <li> dw::core::style::isAbsLength
* <li> dw::core::style::isPerLength
@@ -116,61 +116,61 @@ namespace core {
* <li> dw::core::style::perLengthVal
* <li> dw::core::style::relLengthVal
* </ul>
- *
- *
+ *
+ *
* <h3>Boxes</h3>
- *
+ *
* <h4>The CSS %Box Model</h4>
- *
+ *
* For borders, margins etc., the box model defined by CSS2 is
* used. dw::core::style::Style contains some members defining these
* attributes. A dw::core::Widget must use these values for any
* calculation of sizes. There are some helper functions (see
* dw/style.hh). A dw::core::style::Style box looks quite similar to a
* CSS box:
- *
+ *
* \image html dw-style-box-model.png
- *
+ *
* <h4>Background colors</h4>
- *
+ *
* The background color is stored in
* dw::core::style::Style::backgroundColor, which may be NULL (the
* background color of the parent widget is shining through).
- *
+ *
* For toplevel widgets, this color is set as the background color of the
* views (dw::core::View::setBgColor), for other widgets, a filled
* rectangle is drawn, covering the content and padding. (This is
* compliant with CSS2, the background color of the toplevel element
* covers the whole canvas.)
- *
+ *
* <h4>Drawing</h4>
- *
+ *
* The following methods may be useful:
- *
+ *
* <ul>
* <li> dw::core::Widget::drawWidgetBox for drawing the box of a widget
* (typically at the beginning of the implementation of
* dw::core::Widget::draw), and
- *
+ *
* <li> dw::core::Widget::drawBox, for drawing parts of a widget (e.g.
* dw::Textblock::Word, which has its own dw::Textblock::Word::style).
* </ul>
- *
- *
+ *
+ *
* <h3>Notes on Memory Management</h3>
- *
+ *
* Memory management is done by reference counting,
* dw::core::style::Style::create returns a pointer to
* dw::core::style::Style with an increased reference counter, so you
* should care about calling dw::core::style::Style::unref if it is not
* used anymore. You do \em not need to care about the reference counters
* of fonts and styles.
- *
+ *
* In detail:
- *
+ *
* <ul>
* <li> dw::core::style::Style::ref is called in
- *
+ *
* <ul>
* <li> dw::core::Widget::setStyle to assign a style to a widget,
* <li> dw::Textblock::addText, dw::Textblock::addWidget,
@@ -179,9 +179,9 @@ namespace core {
* to assign a style to a dw::Textblock::Word, and
* <li> by the HTML parser, when pushing an element on the stack.
* </ul>
- *
+ *
* <li> dw::core::style::Style::unref is called in
- *
+ *
* <ul>
* <li> dw::core::Widget::~Widget, dw::Textblock::~Textblock, by the
* HTML parser, when popping an element fom the stack, and
@@ -237,7 +237,9 @@ enum VAlignType {
VALIGN_MIDDLE,
VALIGN_BASELINE,
VALIGN_SUB,
- VALIGN_SUPER
+ VALIGN_SUPER,
+ VALIGN_TEXT_TOP,
+ VALIGN_TEXT_BOTTOM,
};
/**
@@ -247,15 +249,19 @@ enum DisplayType {
DISPLAY_BLOCK,
DISPLAY_INLINE,
DISPLAY_LIST_ITEM,
+ DISPLAY_NONE,
DISPLAY_TABLE,
DISPLAY_TABLE_ROW_GROUP,
DISPLAY_TABLE_HEADER_GROUP,
DISPLAY_TABLE_FOOTER_GROUP,
DISPLAY_TABLE_ROW,
- DISPLAY_TABLE_CELL,
- DISPLAY_LAST
+ DISPLAY_TABLE_CELL
};
+enum ListStylePosition {
+ LIST_STYLE_POSITION_INSIDE,
+ LIST_STYLE_POSITION_OUTSIDE
+};
enum ListStyleType {
LIST_STYLE_TYPE_DISC,
LIST_STYLE_TYPE_CIRCLE,
@@ -297,7 +303,9 @@ enum TextDecoration {
enum WhiteSpace {
WHITE_SPACE_NORMAL,
WHITE_SPACE_PRE,
- WHITE_SPACE_NOWRAP
+ WHITE_SPACE_NOWRAP,
+ WHITE_SPACE_PRE_WRAP,
+ WHITE_SPACE_PRE_LINE,
};
enum FloatType {
@@ -331,15 +339,15 @@ enum ClearType {
* This is an implementation detail, use one of the following functions:
*
* Creating lengths:
- *
+ *
* <ul>
* <li> dw::core::style::createAbsLength
* <li> dw::core::style::createPerLength
* <li> dw::core::style::createRelLength
* </ul>
- *
+ *
* Examine lengths:
- *
+ *
* <ul>
* <li> dw::core::style::isAbsLength
* <li> dw::core::style::isPerLength
@@ -349,7 +357,7 @@ enum ClearType {
* <li> dw::core::style::relLengthVal
* </ul>
*
- * "auto" lenghths are represented as dw::core::style::LENGTH_AUTO.
+ * "auto" lengths are represented as dw::core::style::LENGTH_AUTO.
*/
typedef int Length;
@@ -358,11 +366,11 @@ inline Length createAbsLength(int n) { return (n << 2) | 1; }
/** \brief Returns a percentage, \em v is relative to 1, not to 100. */
inline Length createPerLength(double v) {
- return (int)(v * (1 << 18)) & ~3 | 2; }
+ return ((int)(v * (1 << 18)) & ~3) | 2; }
/** \brief Returns a relative length. */
inline Length createRelLength(double v) {
- return (int)(v * (1 << 18)) & ~3 | 3; }
+ return ((int)(v * (1 << 18)) & ~3) | 3; }
/** \brief Returns true if \em l is an absolute length. */
inline bool isAbsLength(Length l) { return (l & 3) == 1; }
@@ -418,7 +426,7 @@ class Tooltip;
/**
* \sa dw::core::style
*/
-class StyleAttrs : public object::Object
+class StyleAttrs : public lout::object::Object
{
public:
Font *font;
@@ -433,8 +441,8 @@ public:
FloatType vloat; /* "float" is a keyword. */
ClearType clear;
- int hBorderSpacing, vBorderSpacing;
- Length width, height;
+ int hBorderSpacing, vBorderSpacing, wordSpacing;
+ Length width, height, lineHeight;
Box margin, borderWidth, padding;
struct { Color *top, *right, *bottom, *left; } borderColor;
@@ -442,13 +450,14 @@ public:
DisplayType display;
WhiteSpace whiteSpace;
+ ListStylePosition listStylePosition;
ListStyleType listStyleType;
Cursor cursor;
int x_link;
int x_img;
Tooltip *x_tooltip;
-
+
void initValues ();
void resetValues ();
@@ -482,7 +491,7 @@ public:
inline bool hasBackground () { return backgroundColor != NULL; }
- bool equals (object::Object *other);
+ bool equals (lout::object::Object *other);
int hashValue ();
};
@@ -495,7 +504,7 @@ class Style: public StyleAttrs
private:
static int totalRef;
int refCount;
- static container::typed::HashTable <StyleAttrs, Style> *styleTable;
+ static lout::container::typed::HashTable <StyleAttrs, Style> *styleTable;
Style (StyleAttrs *attrs);
@@ -518,17 +527,17 @@ public:
}
inline void ref () { refCount++; }
- inline void unref () { if(--refCount == 0) delete this; }
+ inline void unref () { if (--refCount == 0) delete this; }
};
/**
* \sa dw::core::style
*/
-class TooltipAttrs: public object::String
+class TooltipAttrs: public lout::object::String
{
public:
- TooltipAttrs(const char *text): object::String(text) { }
+ TooltipAttrs(const char *text): lout::object::String(text) { }
};
/**
@@ -539,34 +548,34 @@ class Tooltip: public TooltipAttrs
private:
int refCount;
+protected:
Tooltip (const char *text): TooltipAttrs(text) { refCount = 0; }
public:
- inline Tooltip *create (Layout *layout, const char *text)
- { return new Tooltip (text); }
-
+ static Tooltip *create (dw::core::Layout *layout, const char *text);
inline void ref () { refCount++; }
inline void unref ()
- { if(--refCount == 0) delete this; }
+ { if (--refCount == 0) delete this; }
- inline void onEnter () { }
- inline void onLeave () { }
- inline void onMotion () { }
+ inline virtual void onEnter () { }
+ inline virtual void onLeave () { }
+ inline virtual void onMotion () { }
};
/**
* \sa dw::core::style
*/
-class FontAttrs: public object::Object
+class FontAttrs: public lout::object::Object
{
public:
const char *name;
int size;
int weight;
+ int letterSpacing;
FontStyle style;
- bool equals(object::Object *other);
+ bool equals(lout::object::Object *other);
int hashValue();
};
@@ -593,37 +602,30 @@ public:
int xHeight;
static Font *create (Layout *layout, FontAttrs *attrs);
- static Font *createFromList (Layout *layout, FontAttrs *attrs,
- char *defaultFamily);
+ static bool exists (Layout *layout, const char *name);
inline void ref () { refCount++; }
- inline void unref () { if(--refCount == 0) delete this; }
+ inline void unref () { if (--refCount == 0) delete this; }
};
/**
* \sa dw::core::style
*/
-class ColorAttrs: public object::Object
+class ColorAttrs: public lout::object::Object
{
-public:
- enum Type { TYPE_SIMPLE, TYPE_SHADED };
-
protected:
int color;
- Type type;
public:
- inline ColorAttrs(int color, Type type)
+ inline ColorAttrs(int color)
{
this->color = color;
- this->type = type;
}
inline int getColor () { return color; }
- inline Type getType () { return type; }
- bool equals(object::Object *other);
+ bool equals(lout::object::Object *other);
int hashValue();
};
@@ -636,12 +638,11 @@ class Color: public ColorAttrs
private:
int refCount;
- static Color *create (Layout *layout, int color, Type type);
void remove(dw::core::Layout *layout);
- int shadeColor (int color, int d);
+ int shadeColor (int color, int d);
protected:
- inline Color (int color, Type type): ColorAttrs (color, type) {
+ inline Color (int color): ColorAttrs (color) {
refCount = 0; }
virtual ~Color ();
@@ -653,19 +654,11 @@ protected:
int shadeColor (int color, Shading shading);
public:
- inline static Color *createSimple (Layout *layout, int color)
- {
- return create (layout, color, TYPE_SIMPLE);
- }
-
- inline static Color *createShaded (Layout *layout, int color)
- {
- return create (layout, color, TYPE_SHADED);
- }
+ static Color *create (Layout *layout, int color);
inline void ref () { refCount++; }
inline void unref ()
- { if(--refCount == 0) delete this; }
+ { if (--refCount == 0) delete this; }
};
void drawBorder (View *view, Rectangle *area,
diff --git a/dw/table.cc b/dw/table.cc
index 4135799b..51718587 100644
--- a/dw/table.cc
+++ b/dw/table.cc
@@ -14,17 +14,18 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//#define DBG
#include "table.hh"
+#include "../lout/msg.h"
#include "../lout/misc.hh"
#define MAX misc::max
+using namespace lout;
namespace dw {
@@ -34,6 +35,7 @@ Table::Table(bool limitTextWidth)
{
registerName ("dw::Table", &CLASS_ID);
setFlags (USES_HINTS);
+ setButtonSensitive(false);
this->limitTextWidth = limitTextWidth;
@@ -113,7 +115,7 @@ void Table::sizeRequestImpl (core::Requisition *requisition)
getStyle()->boxDiffHeight () + cumHeight->get (numRows)
+ getStyle()->vBorderSpacing;
requisition->descent = 0;
-
+
}
void Table::getExtremesImpl (core::Extremes *extremes)
@@ -141,10 +143,8 @@ void Table::getExtremesImpl (core::Extremes *extremes)
core::style::absLengthVal(getStyle()->width));
}
-#ifdef DBG
- printf(" Table::getExtremesImpl, {%d, %d} numCols=%d\n",
- extremes->minWidth, extremes->maxWidth, numCols);
-#endif
+ _MSG(" Table::getExtremesImpl, {%d, %d} numCols=%d\n",
+ extremes->minWidth, extremes->maxWidth, numCols);
}
void Table::sizeAllocateImpl (core::Allocation *allocation)
@@ -172,14 +172,14 @@ void Table::sizeAllocateImpl (core::Allocation *allocation)
core::Allocation childAllocation;
core::Requisition childRequisition;
-
+
children->get(n)->cell.widget->sizeRequest (&childRequisition);
childAllocation.x = x;
childAllocation.y = cumHeight->get (row) + offy;
childAllocation.width = width;
childAllocation.ascent = childRequisition.ascent;
- childAllocation.descent =
+ childAllocation.descent =
cumHeight->get (row + children->get(n)->cell.rowspan)
- cumHeight->get (row) - getStyle()->vBorderSpacing
- childRequisition.ascent;
@@ -188,7 +188,7 @@ void Table::sizeAllocateImpl (core::Allocation *allocation)
}
x += colWidths->get (col) + getStyle()->hBorderSpacing;
- }
+ }
}
void Table::resizeDrawImpl ()
@@ -200,12 +200,10 @@ void Table::resizeDrawImpl ()
}
void Table::setWidth (int width)
-{
+{
// If limitTextWidth is set, a queueResize may also be necessary.
if (availWidth != width || limitTextWidth) {
-#ifdef DBG
- printf(" Table::setWidth %d\n", width);
-#endif
+ _MSG(" Table::setWidth %d\n", width);
availWidth = width;
queueResize (0, false);
}
@@ -258,7 +256,7 @@ void Table::draw (core::View *view, core::Rectangle *area)
}
}
}
-
+
void Table::removeChild (Widget *child)
{
/** \bug Not implemented. */
@@ -277,22 +275,22 @@ void Table::addCell (Widget *widget, int colspan, int rowspan)
// We limit the values for colspan and rowspan to 50, to avoid
// attacks by malicious web pages.
if (colspan > 50 || colspan < 0) {
- fprintf (stderr, "WARNING: colspan = %d is set to 50.\n", colspan);
+ MSG_WARN("colspan = %d is set to 50.\n", colspan);
colspan = 50;
}
if (rowspan > 50 || rowspan <= 0) {
- fprintf (stderr, "WARNING: rowspan = %d is set to 50.\n", rowspan);
+ MSG_WARN("rowspan = %d is set to 50.\n", rowspan);
rowspan = 50;
}
if (numRows == 0) {
// to prevent a crash
- fprintf (stderr, "WARNING: Cell without row.\n");
+ MSG("addCell: cell without row.\n");
addRow (NULL);
}
if (rowClosed) {
- fprintf (stderr, "WARNING: Last cell had colspan=0.\n");
+ MSG_WARN("Last cell had colspan=0.\n");
addRow (NULL);
}
@@ -308,10 +306,9 @@ void Table::addCell (Widget *widget, int colspan, int rowspan)
child->type == Child::SPAN_SPACE)
curCol++;
-#ifdef DBG
- printf("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n",
- numCols, curCol, colspan, colspanEff);
-#endif
+ _MSG("Table::addCell numCols=%d,curCol=%d,colspan=%d,colspanEff=%d\n",
+ numCols, curCol, colspan, colspanEff);
+
// Increase children array, when necessary.
if (curRow + rowspan > numRows)
reallocChildren (numCols, curRow + rowspan);
@@ -337,7 +334,7 @@ void Table::addCell (Widget *widget, int colspan, int rowspan)
child->cell.colspanEff = colspanEff;
child->cell.rowspan = rowspan;
children->set (curRow * numCols + curCol, child);
-
+
curCol += colspanEff;
widget->setParent (this);
@@ -351,18 +348,18 @@ void Table::addCell (Widget *widget, int colspan, int rowspan)
for (int col = 0; col < numCols; col++) {
int n = row * numCols + col;
if (!(child = children->get (n))) {
- printf("[null ] ");
+ MSG("[null ] ");
} else if (children->get(n)->type == Child::CELL) {
- printf("[CELL rs=%d] ", child->cell.rowspan);
+ MSG("[CELL rs=%d] ", child->cell.rowspan);
} else if (children->get(n)->type == Child::SPAN_SPACE) {
- printf("[SPAN rs=%d] ", child->cell.rowspan);
+ MSG("[SPAN rs=%d] ", child->cell.rowspan);
} else {
- printf("[Unk. ] ");
+ MSG("[Unk. ] ");
}
}
- printf("\n");
+ MSG("\n");
}
- printf("\n");
+ MSG("\n");
#endif
}
@@ -458,7 +455,7 @@ void Table::reallocChildren (int newNumCols, int newNumRows)
for (int row = numRows; row < newNumRows; row++)
for (int col = 0; col < newNumCols; col++)
children->set (row * newNumCols + col, NULL);
-
+
// Simple arrays.
rowStyle->setSize (newNumRows);
for (int row = numRows; row < newNumRows; row++)
@@ -485,7 +482,7 @@ void Table::forceCalcCellSizes ()
// Will also call calcColumnExtremes(), when needed.
getExtremes (&extremes);
-
+
if (core::style::isAbsLength (getStyle()->width)) {
totalWidth = core::style::absLengthVal (getStyle()->width);
} else if (core::style::isPerLength (getStyle()->width)) {
@@ -503,28 +500,28 @@ void Table::forceCalcCellSizes ()
totalWidth = availWidth;
forceTotalWidth = 0;
}
-#ifdef DBG
- printf(" availWidth = %d\n", availWidth);
- printf(" totalWidth1 = %d\n", totalWidth);
-#endif
+
+ _MSG(" availWidth = %d\n", availWidth);
+ _MSG(" totalWidth1 = %d\n", totalWidth);
+
if (totalWidth < extremes.minWidth)
totalWidth = extremes.minWidth;
totalWidth = totalWidth
- (numCols + 1) * getStyle()->hBorderSpacing
- getStyle()->boxDiffWidth ();
-#ifdef DBG
- printf(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol);
-#endif
+
+ _MSG(" totalWidth2 = %d curCol=%d\n", totalWidth,curCol);
+
colWidths->setSize (numCols, 0);
cumHeight->setSize (numRows + 1, 0);
rowSpanCells->setSize (0);
baseline->setSize (numRows);
-#ifdef DBG
- printf(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth);
- printf(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth());
- printf(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
-#endif
+
+ _MSG(" extremes = %d,%d\n", extremes.minWidth, extremes.maxWidth);
+ _MSG(" getStyle()->boxDiffWidth() = %d\n", getStyle()->boxDiffWidth());
+ _MSG(" getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
+
apportion_percentages2 (totalWidth, forceTotalWidth);
if (!hasColPercent)
@@ -583,10 +580,9 @@ void Table::apportionRowSpan ()
continue;
// Cell size is too small.
-#ifdef DBG
- printf("Short cell %d, sumRows=%d spanHeight=%d\n",
- n,sumRows,spanHeight);
-#endif
+ _MSG("Short cell %d, sumRows=%d spanHeight=%d\n",
+ n,sumRows,spanHeight);
+
// Fill height array
if (!rowHeight) {
rowHeight = new int[numRows];
@@ -594,10 +590,10 @@ void Table::apportionRowSpan ()
rowHeight[i] = cumHeight->get(i+1) - cumHeight->get(i);
}
#ifdef DBG
- printf (" rowHeight { ");
+ MSG(" rowHeight { ");
for (int i = 0; i < numRows; i++)
- printf ("%d ", rowHeight[i]);
- printf ("}\n");
+ MSG("%d ", rowHeight[i]);
+ MSG("}\n");
#endif
// Calc new row sizes for this span.
@@ -607,12 +603,12 @@ void Table::apportionRowSpan ()
sumRows == 0 ? (int)((float)(spanHeight-cumHnew_i)/(row+rs-i)) :
(sumRows-cumh_i) <= 0 ? 0 :
(int)((float)(spanHeight-cumHnew_i)*rowHeight[i]/(sumRows-cumh_i));
-#ifdef DBG
- printf (" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n",
- i,rowHeight[i],hnew_i,
- spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i,
- cumh_i, cumHnew_i);
-#endif
+
+ _MSG(" i=%-3d h=%d hnew_i=%d =%d*%d/%d cumh_i=%d cumHnew_i=%d\n",
+ i,rowHeight[i],hnew_i,
+ spanHeight-cumHnew_i,rowHeight[i],sumRows-cumh_i,
+ cumh_i, cumHnew_i);
+
cumHnew_i += hnew_i;
cumh_i += rowHeight[i];
rowHeight[i] = hnew_i;
@@ -642,9 +638,8 @@ void Table::calcColumnExtremes ()
*/
void Table::forceCalcColumnExtremes ()
{
-#ifdef DBG
- printf(" Table::forceCalcColumnExtremes numCols=%d\n", numCols);
-#endif
+ _MSG(" Table::forceCalcColumnExtremes numCols=%d\n", numCols);
+
if (numCols == 0)
return;
@@ -678,13 +673,13 @@ void Table::forceCalcColumnExtremes ()
cellMinW = cellExtremes.minWidth;
cellMaxW = cellExtremes.maxWidth;
}
-#ifdef DBG
- printf("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n",
- col,
- colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth,
- cellMinW, cellMaxW);
-#endif
+
+ _MSG("FCCE, col%d colMin,colMax,cellMin,cellMax = %d,%d,%d,%d\n",
+ col,
+ colExtremes->getRef(col)->minWidth,
+ colExtremes->getRef(col)->maxWidth,
+ cellMinW, cellMaxW);
+
colExtremes->getRef(col)->minWidth =
MAX (colExtremes->getRef(col)->minWidth, cellMinW);
colExtremes->getRef(col)->maxWidth =
@@ -737,10 +732,10 @@ void Table::forceCalcColumnExtremes ()
minSumCols += colExtremes->getRef(col+i)->minWidth;
maxSumCols += colExtremes->getRef(col+i)->maxWidth;
}
-#ifdef DBG
- printf("cs=%d spanWidth=%d,%d sumCols=%d,%d\n",
- cs,cellMinW,cellMaxW,minSumCols,maxSumCols);
-#endif
+
+ _MSG("cs=%d spanWidth=%d,%d sumCols=%d,%d\n",
+ cs,cellMinW,cellMaxW,minSumCols,maxSumCols);
+
if (minSumCols >= cellMinW && maxSumCols >= cellMaxW)
continue;
@@ -785,7 +780,7 @@ void Table::forceCalcColumnExtremes ()
int d_a = colExtremes->getRef(i)->maxWidth;
int d_w = curAppW > 0 ? (int)((float)curExtraW * d_a/curAppW) : 0;
if (d_a < 0||d_w < 0) {
- printf("d_a=%d d_w=%d\n",d_a,d_w);
+ MSG("d_a=%d d_w=%d\n",d_a,d_w);
exit(1);
}
wMin = colExtremes->getRef(i)->minWidth + d_w;
@@ -809,19 +804,19 @@ void Table::forceCalcColumnExtremes ()
cumMaxWnew += wMax;
cumMaxWold += colExtremes->getRef(i)->maxWidth;
colExtremes->getRef(i)->maxWidth = wMax;
-#ifdef DBG
- printf ("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n",
- i,wMin,wMax,cumMaxWold);
-#endif
+
+ _MSG("i=%d, wMin=%d wMax=%d cumMaxWold=%d\n",
+ i,wMin,wMax,cumMaxWold);
+
}
#ifdef DBG
- printf ("col min,max: [");
+ MSG("col min,max: [");
for (int i = 0; i < numCols; i++)
- printf ("%d,%d ",
- colExtremes->getRef(i)->minWidth,
- colExtremes->getRef(i)->maxWidth);
- printf ("]\n");
- printf ("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
+ MSG("%d,%d ",
+ colExtremes->getRef(i)->minWidth,
+ colExtremes->getRef(i)->maxWidth);
+ MSG("]\n");
+ MSG("getStyle()->hBorderSpacing = %d\n", getStyle()->hBorderSpacing);
#endif
}
}
@@ -835,13 +830,13 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth)
if (colExtremes->size() == 0)
return;
#ifdef DBG
- printf("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
- availWidth, totalWidth, forceTotalWidth);
- printf("app2, extremes: ( ");
+ MSG("app2, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
+ availWidth, totalWidth, forceTotalWidth);
+ MSG("app2, extremes: ( ");
for (int i = 0; i < colExtremes->size (); i++)
- printf("%d,%d ",
- colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
- printf(")\n");
+ MSG("%d,%d ",
+ colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
+ MSG(")\n");
#endif
int minAutoWidth = 0, maxAutoWidth = 0, availAutoWidth = totalWidth;
for (int col = 0; col < numCols; col++) {
@@ -871,10 +866,9 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth)
int curMaxWidth = maxAutoWidth;
int curNewWidth = minAutoWidth;
for (int col = 0; col < numCols; col++) {
-#ifdef DBG
- printf("app2, col %d, minWidth=%d maxWidth=%d\n",
- col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth);
-#endif
+ _MSG("app2, col %d, minWidth=%d maxWidth=%d\n",
+ col,extremes->get(col).minWidth, colExtremes->get(col).maxWidth);
+
if (colPercents->get(col) != LEN_AUTO)
continue;
@@ -882,19 +876,19 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth)
int colMaxWidth = colExtremes->getRef(col)->maxWidth;
int w = (curMaxWidth <= 0) ? 0 :
(int)((float)curTargetWidth * colMaxWidth/curMaxWidth);
-#ifdef DBG
- printf("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d "
- "curNewWidth=%d ",
- curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth);
- printf("w = %d, ", w);
-#endif
+
+ _MSG("app2, curTargetWidth=%d colMaxWidth=%d curMaxWidth=%d "
+ "curNewWidth=%d ",
+ curTargetWidth, colMaxWidth,curMaxWidth,curNewWidth);
+ _MSG("w = %d, ", w);
+
if (w <= colMinWidth)
w = colMinWidth;
else if (curNewWidth - colMinWidth + w > curTargetWidth)
w = colMinWidth + curExtraWidth;
-#ifdef DBG
- printf("w = %d\n", w);
-#endif
+
+ _MSG("w = %d\n", w);
+
curNewWidth -= colMinWidth;
curMaxWidth -= colMaxWidth;
curExtraWidth -= (w - colMinWidth);
@@ -902,10 +896,10 @@ void Table::apportion2 (int totalWidth, int forceTotalWidth)
setColWidth (col, w);
}
#ifdef DBG
- printf("app2, result: ( ");
+ MSG("app2, result: ( ");
for (int i = 0; i < colWidths->size (); i++)
- printf("%d ", colWidths->get (i));
- printf(")\n");
+ MSG("%d ", colWidths->get (i));
+ MSG(")\n");
#endif
}
@@ -917,19 +911,17 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
return;
// If there's a table-wide percentage, totalWidth comes already scaled.
-#ifdef DBG
- printf("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
- availWidth, totalWidth, forceTotalWidth);
-#endif
+ _MSG("APP_P, availWidth=%d, totalWidth=%d, forceTotalWidth=%d\n",
+ availWidth, totalWidth, forceTotalWidth);
if (!hasColPercent) {
#ifdef DBG
- printf("APP_P, only a table-wide percentage\n");
- printf("APP_P, extremes = { ");
+ MSG("APP_P, only a table-wide percentage\n");
+ MSG("APP_P, extremes = { ");
for (int col = 0; col < numCols; col++)
- printf("%d,%d ", colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth);
- printf("}\n");
+ MSG("%d,%d ", colExtremes->getRef(col)->minWidth,
+ colExtremes->getRef(col)->maxWidth);
+ MSG("}\n");
#endif
// It has only a table-wide percentage. Apportion non-absolute widths.
int sumMaxWidth = 0, perAvailWidth = totalWidth;
@@ -939,10 +931,10 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
else
sumMaxWidth += colExtremes->getRef(col)->maxWidth;
}
-#ifdef DBG
- printf("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n",
- perAvailWidth, sumMaxWidth);
-#endif
+
+ _MSG("APP_P, perAvailWidth=%d, sumMaxWidth=%d\n",
+ perAvailWidth, sumMaxWidth);
+
for (int col = 0; col < numCols; col++) {
int max_wi = colExtremes->getRef(col)->maxWidth, new_wi;
if (colPercents->get(col) != LEN_ABS) {
@@ -954,17 +946,16 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
}
}
#ifdef DBG
- printf("APP_P, result = { ");
+ MSG("APP_P, result = { ");
for (int col = 0; col < numCols; col++)
- printf("%d ", result->get(col));
- printf("}\n");
+ MSG("%d ", colWidths->get(col));
+ MSG("}\n");
#endif
} else {
// we'll have to apportion...
-#ifdef DBG
- printf("APP_P, we'll have to apportion...\n");
-#endif
+ _MSG("APP_P, we'll have to apportion...\n");
+
// Calculate cumPercent and available space
float cumPercent = 0.0f;
int hasAutoCol = 0;
@@ -979,11 +970,10 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
}
sumMinWidth += colExtremes->getRef(col)->minWidth;
sumMaxWidth += colExtremes->getRef(col)->maxWidth;
-#ifdef DBG
- printf("APP_P, col %d minWidth=%d maxWidth=%d\n", col,
- colExtremes->getRef(col)->minWidth,
- colExtremes->getRef(col)->maxWidth);
-#endif
+
+ _MSG("APP_P, col %d minWidth=%d maxWidth=%d\n", col,
+ colExtremes->getRef(col)->minWidth,
+ colExtremes->getRef(col)->maxWidth);
}
int oldTotalWidth = totalWidth;
if (!forceTotalWidth) {
@@ -1011,11 +1001,11 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
workingWidth = totalWidth;
curPerWidth = sumMinWidth;
}
-#ifdef DBG
- printf("APP_P, oldTotalWidth=%d totalWidth=%d"
- " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n",
- oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer);
-#endif
+
+ _MSG("APP_P, oldTotalWidth=%d totalWidth=%d"
+ " workingWidth=%d extraWidth=%d sumMinNonPer=%d\n",
+ oldTotalWidth,totalWidth,workingWidth,extraWidth,sumMinNonPer);
+
for (int col = 0; col < numCols; col++) {
int colMinWidth = colExtremes->getRef(col)->minWidth;
if (colPercents->get(col) >= 0.0f) {
@@ -1035,20 +1025,20 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
if (cumPercent < 0.99f) {
// Will have to apportion the other columns
#ifdef DBG
- printf("APP_P, extremes: ( ");
+ MSG("APP_P, extremes: ( ");
for (int i = 0; i < colExtremes->size (); i++)
- printf("%d,%d ",
- colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
- printf(")\n");
+ MSG("%d,%d ",
+ colExtremes->get(i).minWidth, colExtremes->get(i).maxWidth);
+ MSG(")\n");
#endif
curPerWidth -= sumMinNonPer;
int perWidth = (int)(curPerWidth/cumPercent);
totalWidth = MAX (totalWidth, perWidth);
totalWidth = misc::min (totalWidth, oldTotalWidth);
-#ifdef DBG
- printf("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n",
- curPerWidth, perWidth, totalWidth);
-#endif
+
+ _MSG("APP_P, curPerWidth=%d perWidth=%d, totalWidth=%d\n",
+ curPerWidth, perWidth, totalWidth);
+
if (hasAutoCol == 0) {
// Special case, cumPercent < 100% and no other columns to expand.
// We'll honor totalWidth by expanding the percentage cols.
@@ -1062,22 +1052,22 @@ void Table::apportion_percentages2(int totalWidth, int forceTotalWidth)
}
}
#ifdef DBG
- printf("APP_P, result ={ ");
+ MSG("APP_P, result ={ ");
for (int col = 0; col < numCols; col++)
- printf("%d ", colWidths->get(col));
- printf("}\n");
+ MSG("%d ", colWidths->get(col));
+ MSG("}\n");
#endif
apportion2 (totalWidth, 2);
#ifdef DBG
- printf("APP_P, percent={");
+ MSG("APP_P, percent={");
for (int col = 0; col < numCols; col++)
- printf("%f ", colPercents->get(col));
- printf("}\n");
- printf("APP_P, result ={ ");
+ MSG("%f ", colPercents->get(col));
+ MSG("}\n");
+ MSG("APP_P, result ={ ");
for (int col = 0; col < numCols; col++)
- printf("%d ", colWidths->get(col));
- printf("}\n");
+ MSG("%d ", colWidths->get(col));
+ MSG("}\n");
#endif
}
}
@@ -1117,7 +1107,7 @@ int Table::TableIterator::compareTo(misc::Comparable *other)
{
return index - ((TableIterator*)other)->index;
}
-
+
bool Table::TableIterator::next ()
{
Table *table = (Table*)getWidget();
@@ -1130,7 +1120,7 @@ bool Table::TableIterator::next ()
content.type = core::Content::END;
return false;
}
-
+
do {
index++;
if (index >= table->children->size ()) {
@@ -1157,7 +1147,7 @@ bool Table::TableIterator::prev ()
content.type = core::Content::START;
return false;
}
-
+
do {
index--;
if (index < 0) {
diff --git a/dw/table.hh b/dw/table.hh
index ec2bacc8..87bbaa76 100644
--- a/dw/table.hh
+++ b/dw/table.hh
@@ -11,22 +11,22 @@ namespace dw {
* \brief A Widget for rendering tables.
*
* <h3>Introduction</h3>
- *
+ *
* The dw::Table widget is used to render HTML tables.
- *
- * Each cell is itself an own widget. Any widget may be used, however, in
+ *
+ * Each cell is itself a separate widget. Any widget may be used, however, in
* dillo, only instances of dw::Textblock and dw::TableCell are used as
* children of dw::Table.
- *
- *
+ *
+ *
* <h3>Sizes</h3>
- *
+ *
* <h4>General</h4>
- *
+ *
* The following diagram shows the dependencies between the different
* functions, which are related to size calculation. Click on the boxes
- * for more informations.
- *
+ * for more information.
+ *
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
@@ -34,125 +34,125 @@ namespace dw {
* labelfontsize=10, color="#404040", labelfontcolor="#000080",
* fontname=Helvetica, fontsize=10];
* fontname=Helvetica; fontsize=10;
- *
+ *
* sizeRequestImpl [color="#0000ff", URL="\ref dw::Table::sizeRequestImpl"];
* sizeAllocateImpl [color="#0000ff",
* URL="\ref dw::Table::sizeAllocateImpl"];
* getExtremesImpl [color="#0000ff", URL="\ref dw::Table::getExtremesImpl"];
- *
+ *
* subgraph cluster_sizes {
* style="dashed"; color="#8080c0";
* calcCellSizes [URL="\ref dw::Table::calcCellSizes"];
* forceCalcCellSizes [URL="\ref dw::Table::forceCalcCellSizes"];
* }
- *
+ *
* subgraph cluster_extremes {
* style="dashed"; color="#8080c0";
* calcColumnExtremes [URL="\ref dw::Table::calcColumnExtremes"];
* forceCalcColumnExtremes[URL="\ref dw::Table::forceCalcColumnExtremes"];
* }
- *
+ *
* sizeRequestImpl -> forceCalcCellSizes [label="[B]"];
* sizeAllocateImpl -> calcCellSizes [label="[A]"];
* getExtremesImpl -> forceCalcColumnExtremes [label="[B]"];
- *
+ *
* forceCalcCellSizes -> calcColumnExtremes;
- *
+ *
* calcCellSizes -> forceCalcCellSizes [style="dashed", label="[C]"];
* calcColumnExtremes -> forceCalcColumnExtremes [style="dashed",
* label="[C]"];
* }
* \enddot
- *
+ *
* [A] In this case, the new calculation is \em not forced, but only
* done, when necessary.
- *
+ *
* [B] In this case, the new calculation is allways necessary, since [C]
* is the case.
- *
+ *
* [C] Whether this function is called, depends on NEEDS_RESIZE /
* EXTREMES_CHANGED.
- *
- *
+ *
+ *
* <h4>Apportionment</h4>
- *
+ *
* \sa\ref rounding-errors
- *
+ *
* Given two array \f$e_{i,\min}\f$ and \f$e_{i,\max}\f$, which
* represent the column minima and maxima, and a total width \f$W\f$, \em
* apportionment means to calculate column widths \f$w_{i}\f$, with
- *
+ *
* \f[e_{i,\min} \le w_{i} \le e_{i,\max}\f]
- *
+ *
* and
- *
+ *
* \f[\sum w_{i} = W\f]
- *
+ *
* There are different algorithms for apportionment, a simple one is
* recommended in the HTML 4.0.1 specification
* (http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.5.2.2):
- *
+ *
* \f[w_{i} = e_{i,\min} +
* {e_{i,\max} - e_{i,\min}\over\sum e_{i,\max} - \sum e_{i,\min}}
* (W - \sum e_{i,\min})\f]
- *
+ *
* This one is used currently, but another one will be used soon, which is
- * described below. The rest of this chapter is independant of the exact
+ * described below. The rest of this chapter is independent of the exact
* apportionment algorithm.
- *
+ *
* When referring to the apportionment function, we will call it
* \f$a_i (W, (e_{i,\min}), (e_{i,\min}))\f$ and write
* something like this:
- *
+ *
* \f[w_{i} = a_i (W, (e_{i,\min}), (e_{i,\max})) \f]
- *
+ *
* It is implemented by dw::Table::apportion.
- *
+ *
* <h4>Column Extremes</h4>
- *
+ *
* \sa\ref rounding-errors
- *
+ *
* The sizes, which all other sizes depend on, are column extremes, which
* define, how wide a column may be at min and at max. They are
* calculated in the following way:
- *
+ *
* <ol>
* <li> First, only cells with colspan = 1 are regarded:
- *
+ *
* \f[ e_{\hbox{base},i,\min} = \max \{ e_{\hbox{cell},i,j,\min} \} \f]
* \f[ e_{\hbox{base},i,\max} = \max \{ e_{\hbox{cell},i,j,\max} \} \f]
- *
+ *
* only for cells \f$(i, j)\f$ with colspan = 1.
- *
+ *
* <li> Then,
* \f$e_{\hbox{span},i,\min}\f$ (but not \f$e_{\hbox{span},i,\max}\f$)
* are calculated from cells with colspan > 1. (In the following formulas,
* the cell at \f$(i_1, j)\f$ always span from \f$i_1\f$ to \f$i_2\f$.)
- * If the minimal width of the column exeeds the sum of the column minima
+ * If the minimal width of the column exceeds the sum of the column minima
* calculated in the last step:
- *
+ *
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\min}\f]
- *
+ *
* then the minimal width of this cell is apportioned to the columns:
- *
+ *
* <ul>
- * <li> If the minimal width of this cell also exeeds the sum of the
+ * <li> If the minimal width of this cell also exceeds the sum of the
* column maxima:
- *
+ *
* \f[e_{\hbox{cell},i_1,j,\min} >
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}\f]
- *
+ *
* then \f$e_{\hbox{cell},i_1,j,\min}\f$ is apportioned in a simple
* way:
- *
+ *
* \f[e_{\hbox{span},i,j,\min} =
* e_{\hbox{base},i,\max}
* {e_{\hbox{span},i,j,\min} \over
* \sum_{i=i_1}^{i=i_2} e_{\hbox{base},i,\max}}\f]
- *
+ *
* <li> Otherwise, the apportionment function is used:
- *
+ *
* \f[e_{\hbox{span},i,j,\min} =
* a_i (e_{\hbox{cell},i_1,j,\min},
* (e_{\hbox{cell},i_1,j,\min} \ldots
@@ -160,133 +160,133 @@ namespace dw {
* (e_{\hbox{cell},i_1,j,\max} \ldots
* e_{\hbox{cell},i_2,j,\max}))\f]
* </ul>
- *
+ *
* After this, \f$e_{\hbox{span},i,\min}\f$ is then the maximum of all
* \f$e_{\hbox{span},i,j,\min}\f$.
- *
+ *
* <li> Finally, the maximum of both is used.
* \f[ e_{i,\min} =
* \max \{ e_{\hbox{base},i,\min}, e_{\hbox{span},i,\min} \} \f]
* \f[ e_{i,\max} =
* \max \{ e_{\hbox{base},i,\max}, e_{i,\min} \} \f]
* For the maxima, there is no \f$e_{\hbox{span},i,\max}\f$, but it has to
- * be assured, that the maximum is always greater or equal than/to the
+ * be assured, that the maximum is always greater than or equal to the
* minimum.
* </ol>
- *
- * Generally, if absolute widths are speficied, they are, instead of the
+ *
+ * Generally, if absolute widths are specified, they are, instead of the
* results of dw::core::Widget::getExtremes, taken for the minimal and
* maximal width of a cell (minus the box difference, i.e. the difference
* between content size and widget size). If the content width
* specification is smaller than the minimal content width of the widget
* (determined by dw::core::Widget::getExtremes), the latter is used
* instead.
- *
+ *
* If percentage widths are specified, they are also collected, as column
* maxima. A similar method as for the extremes is used, for cells with
* colspan > 1:
- *
+ *
* \f[w_{\hbox{span},i,j,\%} =
* a_i (w_{\hbox{cell},i_1,j,\%},
* (e_{\hbox{cell},i_1,j,\min} \ldots e_{\hbox{cell},i_2,j,\min}),
* (e_{\hbox{cell},i_1,j,\max} \ldots e_{\hbox{cell},i_2,j,\max}))\f]
- *
+ *
* <h4>Cell Sizes</h4>
- *
+ *
* <h5>Determining the Width of the Table</h5>
- *
+ *
* The total width is
- *
+ *
* <ul>
* <li> the specified absolute width of the table, when given, or
- * <li> the available width (set by dw::Table::setWidth) times the specifies
- * percentage width pf t(at max 100%), if the latter is given, or
+ * <li> the available width (set by dw::Table::setWidth) times the specified
+ * percentage width of t(at max 100%), if the latter is given, or
* <li> otherwise the available width.
* </ul>
- *
+ *
* In any case, it is corrected, if it is less than the minimal width
* (but not if it is greater than the maximal width).
- *
- * \bug The parantheses is not fully clear, look at the old code.
- *
+ *
+ * \bug The parentheses is not fully clear, look at the old code.
+ *
* Details on differences because of styles are omitted. Below, this
* total width is called \f$W\f$.
- *
+ *
* <h5>Evaluating percentages</h5>
- *
+ *
* The following algorithms are used to solve collisions between
* different size specifications (absolute and percentage). Generally,
* inherent sizes and specified absolute sizes are preferred.
- *
+ *
* <ol>
* <li> First, calculate the sum of the minimal widths, for columns, where
* no percentage width has been specified. The difference to the total
* width is at max available to the columns with percentage width
* specifications:
- *
+ *
* \f[W_{\hbox{columns}_\%,\hbox{available}} = W - \sum e_{i,\min}\f]
- *
+ *
* with only those columns \f$i\f$ with no percentage width specification.
- *
+ *
* <li> Then, calculate the sum of the widths, which the columns with
* percentage width specification would allocate, when fully adhering to
- * then:
- *
+ * them:
+ *
* \f[W_{\hbox{columns}_\%,\hbox{best}} = W \sum w_{i,\%}\f]
- *
+ *
* with only those columns \f$i\f$ with a percentage width specification.
- *
+ *
* <li> Two cases are distinguished:
- *
+ *
* <ul>
* <li> \f$W_{\hbox{columns}_\%,\hbox{available}} \ge
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the
* percentage widths can be used without any modification, by
* setting the extremes:
- *
+ *
* \f[e_{i,\min} = e_{i,\max} = W w_{i,\%}\f]
- *
+ *
* for only those columns \f$i\f$ with a percentage width
* specification.
- *
+ *
* <li> \f$W_{\hbox{columns}_\%,\hbox{available}} <
* W_{\hbox{columns}_\%,\hbox{best}}\f$: In this case, the widths
* for these columns must be cut down:
- *
+ *
* \f[e_{i,\min} = e_{i,\max} =
* w_{i,\%}
* {W_{\hbox{columns}_\%,\hbox{available}} \over
* w_{\hbox{total},\%}}\f]
- *
+ *
* with
- *
+ *
* \f[w_{\hbox{total},\%} = \sum w_{i,\%}\f]
- *
+ *
* in both cases for only those columns \f$i\f$ with a percentage
- * width specification.
+ * width specification.
* </ul>
* </ol>
- *
+ *
* (\f$e_{i,\min}\f$ and \f$e_{i,\max}\f$ are set \em temporarily here,
* the notation should be a bit clearer.)
- *
- *
+ *
+ *
* <h5>Column Widths</h5>
- *
+ *
* The column widths are now simply calculated by applying the
- * apportenment function.
- *
- *
+ * apportionment function.
+ *
+ *
* <h5>Row Heights</h5>
- *
+ *
* ...
- *
+ *
* <h3>Alternative Apportionment Algorithm</h3>
*
* The algorithm described here tends to result in more homogeneous column
* widths.
*
- * The following rule lead to well-defined \f$w_{i}\f$: All columns
+ * The following rule leads to well-defined \f$w_{i}\f$: All columns
* \f$i\f$ have have the same width \f$w\f$, except:
* <ul>
* <li> \f$w < e_{i,\min}\f$, or
@@ -306,13 +306,13 @@ namespace dw {
* Based on an initial value \f$w = {W\over n}\f$, \f$w\f$ can iteratively
* adjusted, based on these rules.
*
- *
+ *
* <h3>Borders, Paddings, Spacing</h3>
- *
+ *
* Currently, DwTable supports only the separated borders model (see CSS
* specification). Borders, paddings, spacing is done by creating
* dw::core::style::Style structures with values equivalent to following CSS:
- *
+ *
* <pre>
* TABLE {
* border: outset \em table-border;
@@ -320,14 +320,14 @@ namespace dw {
* border-spacing: \em table-cellspacing;
* background-color: \em table-bgcolor;
* }
- *
+ *
* TD TH {
* border: inset \em table-border;
* padding: \em table-cellspacing;
* background-color: \em td/th-bgcolor;
* }
* </pre>
- *
+ *
* Here, \em foo-bar refers to the attribute \em bar of the tag \em foo foo.
* Look at the HTML parser for more details.
*/
@@ -361,8 +361,8 @@ private:
TableIterator (Table *table, core::Content::Type mask, bool atEnd);
TableIterator (Table *table, core::Content::Type mask, int index);
- object::Object *clone();
- int compareTo(misc::Comparable *other);
+ lout::object::Object *clone();
+ int compareTo(lout::misc::Comparable *other);
bool next ();
bool prev ();
@@ -377,37 +377,37 @@ private:
int availWidth, availAscent, availDescent; // set by set...
int numRows, numCols, curRow, curCol;
- misc::SimpleVector<Child*> *children;
+ lout::misc::SimpleVector<Child*> *children;
int redrawX, redrawY;
-
+
/**
* \brief The extremes of all columns.
*/
- misc::SimpleVector<core::Extremes> *colExtremes;
+ lout::misc::SimpleVector<core::Extremes> *colExtremes;
/**
* \brief The widths of all columns.
*/
- misc::SimpleVector<int> *colWidths;
+ lout::misc::SimpleVector<int> *colWidths;
/**
* Row cumulative height array: cumHeight->size() is numRows + 1,
* cumHeight->get(0) is 0, cumHeight->get(numRows) is the total table
* height.
*/
- misc::SimpleVector<int> *cumHeight;
+ lout::misc::SimpleVector<int> *cumHeight;
/**
* If a Cell has rowspan > 1, it goes into this array
*/
- misc::SimpleVector<int> *rowSpanCells;
+ lout::misc::SimpleVector<int> *rowSpanCells;
/**
* If a Cell has colspan > 1, it goes into this array
*/
- misc::SimpleVector<int> *colSpanCells;
- misc::SimpleVector<int> *baseline;
+ lout::misc::SimpleVector<int> *colSpanCells;
+ lout::misc::SimpleVector<int> *baseline;
- misc::SimpleVector<core::style::Style*> *rowStyle;
+ lout::misc::SimpleVector<core::style::Style*> *rowStyle;
/**
* hasColPercent becomes true when any cell specifies a percentage width.
@@ -415,7 +415,7 @@ private:
*/
enum { LEN_AUTO = -1, LEN_ABS = -2};
int hasColPercent;
- misc::SimpleVector<float> *colPercents;
+ lout::misc::SimpleVector<float> *colPercents;
inline bool childDefined(int n)
{
@@ -438,7 +438,7 @@ private:
void setCumHeight (int row, int value)
{
if (value != cumHeight->get (row)) {
- redrawY = misc::min ( redrawY, value );
+ redrawY = lout::misc::min ( redrawY, value );
cumHeight->set (row, value);
}
}
@@ -446,7 +446,7 @@ private:
inline void setColWidth (int col, int value)
{
if (value != colWidths->get (col)) {
- redrawX = misc::min (redrawX, value);
+ redrawX = lout::misc::min (redrawX, value);
colWidths->set (col, value);
}
}
@@ -460,7 +460,7 @@ protected:
void setWidth (int width);
void setAscent (int ascent);
void setDescent (int descent);
- void draw (core::View *view, core::Rectangle *area);
+ void draw (core::View *view, core::Rectangle *area);
//bool buttonPressImpl (core::EventButton *event);
//bool buttonReleaseImpl (core::EventButton *event);
@@ -474,7 +474,7 @@ public:
Table(bool limitTextWidth);
~Table();
- core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
void addCell (Widget *widget, int colspan, int rowspan);
void addRow (core::style::Style *style);
diff --git a/dw/tablecell.cc b/dw/tablecell.cc
index b4d404f7..5b93fe86 100644
--- a/dw/tablecell.cc
+++ b/dw/tablecell.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -29,13 +28,14 @@ int TableCell::CLASS_ID = -1;
TableCell::TableCell (TableCell *ref, bool limitTextWidth):
AlignedTextblock (limitTextWidth)
-{
+{
registerName ("dw::TableCell", &CLASS_ID);
/** \bug ignoreLine1OffsetSometimes does not work? */
//ignoreLine1OffsetSometimes = true;
charWordIndex = -1;
setRefTextblock (ref);
+ setButtonSensitive(true);
}
TableCell::~TableCell()
@@ -45,7 +45,7 @@ TableCell::~TableCell()
void TableCell::wordWrap(int wordIndex)
{
Textblock::Word *word;
- char *p;
+ const char *p;
Textblock::wordWrap (wordIndex);
diff --git a/dw/textblock.cc b/dw/textblock.cc
index 6bfd4d90..33d3a1b0 100644
--- a/dw/textblock.cc
+++ b/dw/textblock.cc
@@ -14,18 +14,20 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "textblock.hh"
#include "table.hh" // Yes, this is ugly. -- SG
+#include "../lout/msg.h"
#include "../lout/misc.hh"
#include <stdio.h>
#include <math.h> // remove again
#include <limits.h>
+using namespace lout;
+
namespace dw {
int Textblock::CLASS_ID = -1;
@@ -34,15 +36,16 @@ Textblock::Textblock (bool limitTextWidth)
{
registerName ("dw::Textblock", &CLASS_ID);
setFlags (USES_HINTS);
+ setButtonSensitive(true);
- listItem = false;
+ hasListitemValue = false;
innerPadding = 0;
line1Offset = 0;
line1OffsetEff = 0;
ignoreLine1OffsetSometimes = false;
mustQueueResize = false;
redrawY = 0;
- lastWordDrawn = 0;
+ lastWordDrawn = -1;
/*
* The initial sizes of lines and words should not be
@@ -56,6 +59,7 @@ Textblock::Textblock (bool limitTextWidth)
lines = new misc::SimpleVector <Line> (1);
words = new misc::SimpleVector <Word> (1);
leftFloatSide = rightFloatSide = NULL;
+ anchors = new misc::SimpleVector <Anchor> (1);
//DBG_OBJ_SET_NUM(page, "num_lines", num_lines);
@@ -97,17 +101,20 @@ Textblock::~Textblock ()
Word *word = words->getRef (i);
if (word->content.type == core::Content::WIDGET)
delete word->content.widget;
- else if (word->content.type == core::Content::ANCHOR)
- /* This also frees the names (see removeAnchor() and related). */
- removeAnchor(word->content.anchor);
-
word->style->unref ();
word->spaceStyle->unref ();
}
+ for (int i = 0; i < anchors->size(); i++) {
+ Anchor *anchor = anchors->getRef (i);
+ /* This also frees the names (see removeAnchor() and related). */
+ removeAnchor(anchor->name);
+ }
+
delete lines;
delete words;
-
+ delete anchors;
+
if(leftFloatSide)
delete leftFloatSide;
if(rightFloatSide)
@@ -133,11 +140,12 @@ void Textblock::sizeRequestImpl (core::Requisition *requisition)
Line *lastLine = lines->getRef (lines->size () - 1);
requisition->width =
misc::max (lastLine->maxLineWidth, lastLineWidth);
- /* Note: the break_space of the last line is ignored, so breaks
+ /* Note: the breakSpace of the last line is ignored, so breaks
at the end of a textblock are not visible. */
- requisition->ascent = lines->getRef(0)->ascent;
+ requisition->ascent = lines->getRef(0)->boxAscent;
requisition->descent = lastLine->top
- + lastLine->ascent + lastLine->descent - lines->getRef(0)->ascent;
+ + lastLine->boxAscent + lastLine->boxDescent -
+ lines->getRef(0)->boxAscent;
} else {
requisition->width = lastLineWidth;
requisition->ascent = 0;
@@ -237,21 +245,23 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
for (lineIndex = wrapRef; lineIndex < lines->size (); lineIndex++) {
//DBG_MSGF (widget, "extremes", 0, "line %d", lineIndex);
//DBG_MSG_START (widget);
+ core::style::WhiteSpace ws;
line = lines->getRef (lineIndex);
- nowrap =
- words->getRef(line->firstWord)->style->whiteSpace
- != core::style::WHITE_SPACE_NORMAL;
+ ws = words->getRef(line->firstWord)->style->whiteSpace;
+ nowrap = ws == core::style::WHITE_SPACE_PRE ||
+ ws == core::style::WHITE_SPACE_NOWRAP;
//DEBUG_MSG (DEBUG_SIZE_LEVEL, " line %d (of %d), nowrap = %d\n",
// lineIndex, page->num_lines, nowrap);
- for (wordIndex = line->firstWord; wordIndex < line->lastWord;
+ for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
word = words->getRef (wordIndex);
getWordExtremes (word, &wordExtremes);
/* For the first word, we simply add the line1_offset. */
+ /* This test looks questionable */
if (ignoreLine1OffsetSometimes && wordIndex == 0) {
wordExtremes.minWidth += line1Offset;
//DEBUG_MSG (DEBUG_SIZE_LEVEL + 1,
@@ -266,8 +276,8 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
extremes->minWidth = wordExtremes.minWidth;
}
- //printf("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n",
- // parMax, wordExtremes.maxWidth, prevWordSpace);
+ _MSG("parMax = %d, wordMaxWidth=%d, prevWordSpace=%d\n",
+ parMax, wordExtremes.maxWidth, prevWordSpace);
if (word->content.type != core::Content::BREAK)
parMax += prevWordSpace;
parMax += wordExtremes.maxWidth;
@@ -279,11 +289,9 @@ void Textblock::getExtremesImpl (core::Extremes *extremes)
// word_extremes.maxWidth);
}
- if ((line->lastWord > line->firstWord &&
- words->getRef(line->lastWord - 1)->content.type
+ if ((words->getRef(line->lastWord)->content.type
== core::Content::BREAK ) ||
lineIndex == lines->size () - 1 ) {
- word = words->getRef (line->lastWord - 1);
//DEBUG_MSG (DEBUG_SIZE_LEVEL + 2,
// " parMax = %d, after word %d (%s)\n",
@@ -335,7 +343,6 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
int xCursor;
core::Allocation childAllocation;
core::Allocation *oldChildAllocation;
- int wordInLine;
if (allocation->width != this->allocation.width) {
redrawY = 0;
@@ -345,17 +352,15 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
line = lines->getRef (lineIndex);
xCursor = lineXOffsetWidget (line);
- wordInLine = 0;
- for (wordIndex = line->firstWord; wordIndex < line->lastWord;
+ for (wordIndex = line->firstWord; wordIndex <= line->lastWord;
wordIndex++) {
word = words->getRef (wordIndex);
- if (wordIndex == lastWordDrawn) {
+ if (wordIndex == lastWordDrawn + 1) {
redrawY = misc::min (redrawY, lineYOffsetWidget (line));
}
- switch (word->content.type) {
- case core::Content::WIDGET:
+ if (word->content.type == core::Content::WIDGET) {
/** \todo Justification within the line is done here. */
childAllocation.x = xCursor + allocation->x;
/* align=top:
@@ -367,7 +372,7 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
* http://www.dillo.org/test/img/ */
childAllocation.y =
lineYOffsetCanvasAllocation (line, allocation)
- + (line->ascent - word->size.ascent);
+ + (line->boxAscent - word->size.ascent);
// - word->content.widget->getStyle()->margin.top;
childAllocation.width = word->size.width;
childAllocation.ascent = word->size.ascent;
@@ -376,9 +381,9 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
// + word->content.widget->getStyle()->margin.bottom;
oldChildAllocation = word->content.widget->getAllocation();
-
- if (childAllocation.x != oldChildAllocation->x ||
- childAllocation.y != oldChildAllocation->y ||
+
+ if (childAllocation.x != oldChildAllocation->x ||
+ childAllocation.y != oldChildAllocation->y ||
childAllocation.width != oldChildAllocation->width) {
/* The child widget has changed its position or its width
* so we need to redraw from this line onwards.
@@ -397,38 +402,53 @@ void Textblock::sizeAllocateImpl (core::Allocation *allocation)
* child might be a table covering the whole page so we would
* end up redrawing the whole screen over and over.
* The drawing of the child content is left to the child itself.
+ * However this optimization is only possible if the widget is
+ * the only word in the line apart from an optional BREAK.
+ * Otherwise the height change of the widget could change the
+ * position of other words in the line, requiring a
+ * redraw of the complete line.
*/
- int childChangedY =
- misc::min(childAllocation.y - allocation->y +
- childAllocation.ascent + childAllocation.descent,
- oldChildAllocation->y - this->allocation.y +
- oldChildAllocation->ascent + oldChildAllocation->descent);
-
- redrawY = misc::min (redrawY, childChangedY);
+ if (line->lastWord == line->firstWord ||
+ (line->lastWord == line->firstWord + 1 &&
+ words->getRef (line->lastWord)->content.type ==
+ core::Content::BREAK)) {
+
+ int childChangedY =
+ misc::min(childAllocation.y - allocation->y +
+ childAllocation.ascent + childAllocation.descent,
+ oldChildAllocation->y - this->allocation.y +
+ oldChildAllocation->ascent +
+ oldChildAllocation->descent);
+
+ redrawY = misc::min (redrawY, childChangedY);
+ } else {
+ redrawY = misc::min (redrawY, lineYOffsetWidget (line));
+ }
}
-
word->content.widget->sizeAllocate (&childAllocation);
- break;
-
- case core::Content::ANCHOR:
- changeAnchor (word->content.anchor,
- lineYOffsetCanvasAllocation (line, allocation));
- break;
-
- default:
- wordInLine++;
- // make compiler happy
- break;
}
xCursor += (word->size.width + word->effSpace);
}
}
-
+
if(leftFloatSide)
leftFloatSide->sizeAllocate(allocation);
if(rightFloatSide)
rightFloatSide->sizeAllocate(allocation);
+
+ for (int i = 0; i < anchors->size(); i++) {
+ Anchor *anchor = anchors->getRef(i);
+ int y;
+
+ if (anchor->wordIndex >= words->size()) {
+ y = allocation->y + allocation->ascent + allocation->descent;
+ } else {
+ Line *line = lines->getRef(findLineOfWord (anchor->wordIndex));
+ y = lineYOffsetCanvasAllocation (line, allocation);
+ }
+ changeAnchor (anchor->name, y);
+ }
}
void Textblock::resizeDrawImpl ()
@@ -594,28 +614,22 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event)
if (event->state & core::BUTTON1_MASK)
return sendSelectionEvent (core::SelectionState::BUTTON_MOTION, event);
else {
- int linkOld, wordIndex;
- core::style::Tooltip *tooltipOld;
-
- wordIndex = findWord (event->xWidget, event->yWidget);
+ bool inSpace;
+ int linkOld = hoverLink;
+ core::style::Tooltip *tooltipOld = hoverTooltip;
+ const Word *word = findWord (event->xWidget, event->yWidget, &inSpace);
// cursor from word or widget style
- if (wordIndex == -1)
+ if (word == NULL) {
setCursor (getStyle()->cursor);
- else
- setCursor (words->getRef(wordIndex)->style->cursor);
-
- linkOld = hoverLink;
- tooltipOld = hoverTooltip;
-
- if (wordIndex == -1) {
hoverLink = -1;
hoverTooltip = NULL;
} else {
- hoverLink = words->getRef(wordIndex)->style->x_link;
- hoverTooltip = words->getRef(wordIndex)->style->x_tooltip;
+ core::style::Style *style = inSpace ? word->spaceStyle : word->style;
+ setCursor (style->cursor);
+ hoverLink = style->x_link;
+ hoverTooltip = style->x_tooltip;
}
-
// Show/hide tooltip
if (tooltipOld != hoverTooltip) {
if (tooltipOld)
@@ -626,7 +640,7 @@ bool Textblock::motionNotifyImpl (core::EventMotion *event)
hoverTooltip->onMotion ();
if (hoverLink != linkOld)
- return emitLinkEnter (hoverLink, -1, -1, -1);
+ return layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
else
return hoverLink != -1;
}
@@ -639,7 +653,11 @@ void Textblock::enterNotifyImpl (core::EventCrossing *event)
void Textblock::leaveNotifyImpl (core::EventCrossing *event)
{
hoverLink = -1;
- (void) emitLinkEnter (hoverLink, -1, -1, -1);
+ (void) layout->emitLinkEnter (this, hoverLink, -1, -1, -1);
+ if (hoverTooltip) {
+ hoverTooltip->onLeave();
+ hoverTooltip = NULL;
+ }
}
/**
@@ -651,114 +669,110 @@ bool Textblock::sendSelectionEvent (core::SelectionState::EventType eventType,
core::Iterator *it;
Line *line, *lastLine;
int nextWordStartX, wordStartX, wordX, nextWordX, yFirst, yLast;
- int charPos = 0, prevPos, wordIndex, lineIndex, link;
+ int charPos = 0, link = -1, prevPos, wordIndex, lineIndex;
Word *word;
- bool found, withinContent, r;
-
- if (words->size () == 0)
- // no contens at all
- return false;
+ bool found, r, withinContent = true;
- // In most cases true, so set here:
- link = -1;
- withinContent = true;
-
- lastLine = lines->getRef (lines->size () - 1);
- yFirst = lineYOffsetCanvasI (0);
- yLast =
- lineYOffsetCanvas (lastLine) + lastLine->ascent + lastLine->descent;
- if (event->yCanvas < yFirst) {
- // Above the first line: take the first word.
- withinContent = false;
- wordIndex = 0;
- charPos = 0;
- } else if (event->yCanvas >= yLast) {
- // Below the last line: take the last word.
+ if (words->size () == 0) {
withinContent = false;
- wordIndex = words->size () - 1;
- word = words->getRef (wordIndex);
- charPos = word->content.type == core::Content::TEXT ?
- strlen (word->content.text) : 0;
+ wordIndex = -1;
} else {
- lineIndex = findLineIndex (event->yWidget);
- line = lines->getRef (lineIndex);
-
- // Pointer within the break space?
- if (event->yWidget >
- (lineYOffsetWidget (line) + line->ascent + line->descent)) {
- // Choose this break.
+ lastLine = lines->getRef (lines->size () - 1);
+ yFirst = lineYOffsetCanvasI (0);
+ yLast = lineYOffsetCanvas (lastLine) + lastLine->boxAscent +
+ lastLine->boxDescent;
+ if (event->yCanvas < yFirst) {
+ // Above the first line: take the first word.
withinContent = false;
- wordIndex = line->lastWord - 1;
+ wordIndex = 0;
charPos = 0;
- } else if (event->xWidget < lineXOffsetWidget (line)) {
- // Left of the first word in the line.
- wordIndex = line->firstWord;
+ } else if (event->yCanvas >= yLast) {
+ // Below the last line: take the last word.
withinContent = false;
- charPos = 0;
+ wordIndex = words->size () - 1;
+ word = words->getRef (wordIndex);
+ charPos = word->content.type == core::Content::TEXT ?
+ strlen (word->content.text) : 0;
} else {
- nextWordStartX = lineXOffsetWidget (line);
- found = false;
- for (wordIndex = line->firstWord;
- !found && wordIndex < line->lastWord;
- wordIndex++) {
- word = words->getRef (wordIndex);
- wordStartX = nextWordStartX;
- nextWordStartX += word->size.width + word->effSpace;
-
- if (event->xWidget >= wordStartX &&
- event->xWidget < nextWordStartX) {
- // We have found the word.
- if (word->content.type == core::Content::TEXT) {
- // Search the character the mouse pointer is in.
- // nextWordX is the right side of this character.
- charPos = 0;
- while ((nextWordX = wordStartX +
- layout->textWidth (word->style->font,
- word->content.text, charPos))
- <= event->xWidget)
- charPos = layout->nextGlyph (word->content.text, charPos);
-
- // The left side of this character.
- prevPos = layout->prevGlyph (word->content.text, charPos);
- wordX = wordStartX + layout->textWidth (word->style->font,
- word->content.text,
- prevPos);
-
- // If the mouse pointer is left from the middle, use the left
- // position, otherwise, use the right one.
- if (event->xWidget <= (wordX + nextWordX) / 2)
- charPos = prevPos;
- } else {
- // Depends on whether the pointer is within the left or
- // right half of the (non-text) word.
- if (event->xWidget >=
- (wordStartX + nextWordStartX) / 2)
- charPos = core::SelectionState::END_OF_WORD;
- else
+ lineIndex = findLineIndex (event->yWidget);
+ line = lines->getRef (lineIndex);
+
+ // Pointer within the break space?
+ if (event->yWidget >
+ (lineYOffsetWidget (line) + line->boxAscent + line->boxDescent)) {
+ // Choose this break.
+ withinContent = false;
+ wordIndex = line->lastWord;
+ charPos = 0;
+ } else if (event->xWidget < lineXOffsetWidget (line)) {
+ // Left of the first word in the line.
+ wordIndex = line->firstWord;
+ withinContent = false;
+ charPos = 0;
+ } else {
+ nextWordStartX = lineXOffsetWidget (line);
+ found = false;
+ for (wordIndex = line->firstWord;
+ !found && wordIndex <= line->lastWord;
+ wordIndex++) {
+ word = words->getRef (wordIndex);
+ wordStartX = nextWordStartX;
+ nextWordStartX += word->size.width + word->effSpace;
+
+ if (event->xWidget >= wordStartX &&
+ event->xWidget < nextWordStartX) {
+ // We have found the word.
+ if (word->content.type == core::Content::TEXT) {
+ // Search the character the mouse pointer is in.
+ // nextWordX is the right side of this character.
charPos = 0;
+ while ((nextWordX = wordStartX +
+ layout->textWidth (word->style->font,
+ word->content.text, charPos))
+ <= event->xWidget)
+ charPos = layout->nextGlyph (word->content.text,
+ charPos);
+ // The left side of this character.
+ prevPos = layout->prevGlyph (word->content.text, charPos);
+ wordX = wordStartX + layout->textWidth (word->style->font,
+ word->content.text,
+ prevPos);
+
+ // If the mouse pointer is left from the middle, use the
+ // left position, otherwise, use the right one.
+ if (event->xWidget <= (wordX + nextWordX) / 2)
+ charPos = prevPos;
+ } else {
+ // Depends on whether the pointer is within the left or
+ // right half of the (non-text) word.
+ if (event->xWidget >=
+ (wordStartX + nextWordStartX) / 2)
+ charPos = core::SelectionState::END_OF_WORD;
+ else
+ charPos = 0;
+ }
+
+ found = true;
+ link = word->style ? word->style->x_link : -1;
+ break;
}
-
- found = true;
- link = word->style ? word->style->x_link : -1;
- break;
}
- }
- if (!found) {
- // No word found in this line (i.e. we are on the right side),
- // take the last of this line.
- withinContent = false;
- wordIndex = line->lastWord - 1;
- if (wordIndex >= words->size ())
- wordIndex--;
- word = words->getRef (wordIndex);
- charPos = word->content.type == core::Content::TEXT ?
- strlen (word->content.text) :
- (int)core::SelectionState::END_OF_WORD;
+ if (!found) {
+ // No word found in this line (i.e. we are on the right side),
+ // take the last of this line.
+ withinContent = false;
+ wordIndex = line->lastWord;
+ if (wordIndex >= words->size ())
+ wordIndex--;
+ word = words->getRef (wordIndex);
+ charPos = word->content.type == core::Content::TEXT ?
+ strlen (word->content.text) :
+ (int)core::SelectionState::END_OF_WORD;
+ }
}
}
}
-
it = new TextblockIterator (this, core::Content::SELECTION_CONTENT,
wordIndex);
r = selectionHandleEvent (eventType, it, charPos, link, event,
@@ -794,12 +808,12 @@ void Textblock::justifyLine (Line *line, int availWidth)
diff = availWidth - lastLineWidth;
if (diff > 0) {
origSpaceSum = 0;
- for (i = line->firstWord; i < line->lastWord - 1; i++)
+ for (i = line->firstWord; i < line->lastWord; i++)
origSpaceSum += words->getRef(i)->origSpace;
origSpaceCum = 0;
lastEffSpaceDiffCum = 0;
- for (i = line->firstWord; i < line->lastWord - 1; i++) {
+ for (i = line->firstWord; i < line->lastWord; i++) {
origSpaceCum += words->getRef(i)->origSpace;
if (origSpaceCum == 0)
@@ -818,9 +832,9 @@ void Textblock::justifyLine (Line *line, int availWidth)
}
-void Textblock::addLine (int wordInd, bool newPar)
+Textblock::Line *Textblock::addLine (int wordIndex, bool newPar)
{
- Line *lastLine, *plastLine;
+ Line *lastLine;
//DBG_MSG (page, "wrap", 0, "Dw_page_add_line");
//DBG_MSG_START (page);
@@ -833,29 +847,19 @@ void Textblock::addLine (int wordInd, bool newPar)
lastLine = lines->getRef (lines->size () - 1);
- if (lines->size () == 1)
- plastLine = NULL;
- else
- plastLine = lines->getRef (lines->size () - 2);
-
- if (plastLine) {
- /* second or more lines: copy values of last line */
- lastLine->top =
- plastLine->top + plastLine->ascent +
- plastLine->descent + plastLine->breakSpace;
- lastLine->maxLineWidth = plastLine->maxLineWidth;
- lastLine->maxWordMin = plastLine->maxWordMin;
- lastLine->maxParMax = plastLine->maxParMax;
- lastLine->parMin = plastLine->parMin;
- lastLine->parMax = plastLine->parMax;
- } else {
- /* first line: initialize values */
+ if (lines->size () == 1) {
lastLine->top = 0;
lastLine->maxLineWidth = line1OffsetEff;
lastLine->maxWordMin = 0;
lastLine->maxParMax = 0;
- lastLine->parMin = line1OffsetEff;
- lastLine->parMax = line1OffsetEff;
+ } else {
+ Line *prevLine = lines->getRef (lines->size () - 2);
+
+ lastLine->top = prevLine->top + prevLine->boxAscent +
+ prevLine->boxDescent + prevLine->breakSpace;
+ lastLine->maxLineWidth = prevLine->maxLineWidth;
+ lastLine->maxWordMin = prevLine->maxWordMin;
+ lastLine->maxParMax = prevLine->maxParMax;
}
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.top", page->num_lines - 1,
@@ -871,9 +875,9 @@ void Textblock::addLine (int wordInd, bool newPar)
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.parMax", page->num_lines - 1,
// lastLine->parMax);
- lastLine->firstWord = wordInd;
- lastLine->ascent = 0;
- lastLine->descent = 0;
+ lastLine->firstWord = wordIndex;
+ lastLine->boxAscent = lastLine->contentAscent = 0;
+ lastLine->boxDescent = lastLine->contentDescent = 0;
lastLine->marginDescent = 0;
lastLine->breakSpace = 0;
lastLine->leftOffset = 0;
@@ -881,9 +885,9 @@ void Textblock::addLine (int wordInd, bool newPar)
lastLine->boxRight = 0;
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
- // lastLine->ascent);
+ // lastLine->boxAscent);
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
- // lastLine->descent);
+ // lastLine->boxDescent);
/* update values in line */
lastLine->maxLineWidth = misc::max (lastLine->maxLineWidth, lastLineWidth);
@@ -898,6 +902,10 @@ void Textblock::addLine (int wordInd, bool newPar)
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.maxParMax", page->num_lines - 1,
// lastLine->maxParMax);
+ /* The following code looks questionable (especially since the values
+ * will be overwritten). In any case, line1OffsetEff is probably
+ * supposed to go into lastLinePar*, not lastLine->par*.
+ */
if (lines->size () > 1) {
lastLine->parMin = 0;
lastLine->parMax = 0;
@@ -921,18 +929,19 @@ void Textblock::addLine (int wordInd, bool newPar)
// lastLine->parMax);
//DBG_MSG_END (page);
+ return lastLine;
}
/*
- * This method is called in two cases: (i) when a word is added (by
- * Dw_page_add_word), and (ii) when a page has to be (partially)
- * rewrapped. It does word wrap, and adds new lines, if necesary.
+ * This method is called in two cases: (i) when a word is added
+ * (ii) when a page has to be (partially) rewrapped. It does word wrap,
+ * and adds new lines if necessary.
*/
void Textblock::wordWrap(int wordIndex)
{
Line *lastLine;
- Word *word, *prevWord;
- int availWidth, lastSpace, leftOffset;
+ Word *word;
+ int availWidth, lastSpace, leftOffset, len;
bool newLine = false, newPar = false;
core::Extremes wordExtremes;
@@ -948,6 +957,17 @@ void Textblock::wordWrap(int wordIndex)
availWidth = layout->getWidthViewport () - 10;
word = words->getRef (wordIndex);
+ word->effSpace = word->origSpace;
+
+ /* Test whether line1Offset can be used. */
+ if (wordIndex == 0) {
+ if (ignoreLine1OffsetSometimes &&
+ line1Offset + word->size.width > availWidth) {
+ line1OffsetEff = 0;
+ } else {
+ line1OffsetEff = line1Offset;
+ }
+ }
if(word->content.type == dw::core::Content::FLOAT_REF)
{
@@ -955,10 +975,12 @@ void Textblock::wordWrap(int wordIndex)
int y =
allocation.y - containingBox->allocation.y + getStyle()->boxOffsetY() +
(line ? line->top : 0);
- int lineHeight = line ? line->ascent + line->descent : 0;
+ int lineHeight = line ? line->boxAscent + line->boxDescent : 0;
containingBox->handleFloatInContainer(word->content.widget, misc::max(lines->size() - 1, 0), y, lastLineWidth, lineHeight);
}
+
+
if (lines->size () == 0) {
//DBG_MSG (page, "wrap", 0, "first line");
@@ -966,72 +988,61 @@ void Textblock::wordWrap(int wordIndex)
newPar = true;
lastLine = NULL;
} else {
+ Word *prevWord = words->getRef (wordIndex - 1);
+
lastLine = lines->getRef (lines->size () - 1);
- if (lines->size () > 0) {
- prevWord = words->getRef (wordIndex - 1);
- if (prevWord->content.type == core::Content::BREAK) {
- //DBG_MSG (page, "wrap", 0, "after a break");
- /* previous word is a break */
- newLine = true;
- newPar = true;
- } else if (word->style->whiteSpace
- != core::style::WHITE_SPACE_NORMAL) {
- //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)",
- // word->style->white_space);
- newLine = false;
- newPar = false;
- } else {
- if (lastLine->firstWord != wordIndex) {
- /* Does new word fit into the last line? */
- //DBG_MSGF (page, "wrap", 0,
- // "word %d (%s) fits? (%d + %d + %d &lt;= %d)...",
- // word_ind, a_Dw_content_html (&word->content),
- // page->lastLine_width, prevWord->orig_space,
- // word->size.width, availWidth);
- newLine = (lastLineWidth + prevWord->origSpace +
- word->size.width
- > availWidth -
- (lastLine->boxLeft + lastLine->boxRight));
- //DBG_MSGF (page, "wrap", 0, "... %s.",
- // newLine ? "No" : "Yes");
- }
- }
+ if (prevWord->content.type == core::Content::BREAK) {
+ //DBG_MSG (page, "wrap", 0, "after a break");
+ /* previous word is a break */
+ newLine = true;
+ newPar = true;
+ } else if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP ||
+ word->style->whiteSpace == core::style::WHITE_SPACE_PRE) {
+ //DBG_MSGF (page, "wrap", 0, "no wrap (white_space = %d)",
+ // word->style->white_space);
+ newLine = false;
+ newPar = false;
+ } else if (lastLine->firstWord != wordIndex) {
+ /* Does new word fit into the last line? */
+ //DBG_MSGF (page, "wrap", 0,
+ // "word %d (%s) fits? (%d + %d + %d &lt;= %d)...",
+ // word_ind, a_Dw_content_html (&word->content),
+ // page->lastLine_width, prevWord->orig_space,
+ // word->size.width, availWidth);
+ newLine = lastLineWidth + prevWord->origSpace + word->size.width >
+ availWidth - (lastLine->boxLeft + lastLine->boxRight);
+ //DBG_MSGF (page, "wrap", 0, "... %s.",
+ // newLine ? "No" : "Yes");
}
}
- /* Has sometimes the wrong value. */
- word->effSpace = word->origSpace;
- //DBG_OBJ_ARRSET_NUM (page,"words.%d.eff_space", word_ind, word->eff_space);
-
- /* Test, whether line1_offset can be used. */
- if (wordIndex == 0) {
- if (ignoreLine1OffsetSometimes) {
- if (line1Offset + word->size.width > availWidth)
- line1OffsetEff = 0;
- else
- line1OffsetEff = line1Offset;
- } else
- line1OffsetEff = line1Offset;
+ if (newLine) {
+ if (word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY &&
+ lastLine != NULL && !newPar) {
+ justifyLine (lastLine, availWidth);
+ }
+ lastLine = addLine (wordIndex, newPar);
}
- if (lastLine != NULL && newLine && !newPar &&
- word->style->textAlign == core::style::TEXT_ALIGN_JUSTIFY)
- justifyLine (lastLine, availWidth);
+ lastLine->lastWord = wordIndex;
+ lastLine->boxAscent = misc::max (lastLine->boxAscent, word->size.ascent);
+ lastLine->boxDescent = misc::max (lastLine->boxDescent, word->size.descent);
- if (newLine) {
- addLine (wordIndex, newPar);
- lastLine = lines->getRef (lines->size () - 1);
- }
+ len = word->style->font->ascent;
+ if (word->style->valign == core::style::VALIGN_SUPER)
+ len += len / 2;
+ lastLine->contentAscent = misc::max (lastLine->contentAscent, len);
- lastLine->lastWord = wordIndex + 1;
- lastLine->ascent = misc::max (lastLine->ascent, (int) word->size.ascent);
- lastLine->descent = misc::max (lastLine->descent, (int) word->size.descent);
+ len = word->style->font->descent;
+ if (word->style->valign == core::style::VALIGN_SUB)
+ len += word->style->font->ascent / 3;
+ lastLine->contentDescent = misc::max (lastLine->contentDescent, len);
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
- // lastLine->ascent);
+ // lastLine->boxAscent);
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.descent", page->num_lines - 1,
- // lastLine->descent);
+ // lastLine->boxDescent);
if (word->content.type == core::Content::WIDGET) {
lastLine->marginDescent =
@@ -1049,49 +1060,52 @@ void Textblock::wordWrap(int wordIndex)
/* Here, we know already what the break and the bottom margin
* contributed to the space before this line.
*/
- lastLine->ascent =
- misc::max (lastLine->ascent,
+ lastLine->boxAscent =
+ misc::max (lastLine->boxAscent,
word->size.ascent
+ word->content.widget->getStyle()->margin.top);
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.ascent", page->num_lines - 1,
- // lastLine->ascent);
+ // lastLine->boxAscent);
}
- } else
+ } else {
lastLine->marginDescent =
- misc::max (lastLine->marginDescent, lastLine->descent);
+ misc::max (lastLine->marginDescent, lastLine->boxDescent);
- getWordExtremes (word, &wordExtremes);
- lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0;
+ if (word->content.type == core::Content::BREAK)
+ lastLine->breakSpace =
+ misc::max (word->content.breakSpace,
+ lastLine->marginDescent - lastLine->boxDescent,
+ lastLine->breakSpace);
+ }
- if (word->content.type == core::Content::BREAK)
- lastLine->breakSpace =
- misc::max (word->content.breakSpace,
- lastLine->marginDescent - lastLine->descent,
- lastLine->breakSpace);
+ lastSpace = (wordIndex > 0) ? words->getRef(wordIndex - 1)->origSpace : 0;
- lastLineWidth += word->size.width;
if (!newLine)
lastLineWidth += lastSpace;
-
- lastLineParMin += wordExtremes.maxWidth;
- lastLineParMax += wordExtremes.maxWidth;
if (!newPar) {
lastLineParMin += lastSpace;
lastLineParMax += lastSpace;
}
- if (word->style->whiteSpace != core::style::WHITE_SPACE_NORMAL) {
+ lastLineWidth += word->size.width;
+
+ getWordExtremes (word, &wordExtremes);
+ lastLineParMin += wordExtremes.maxWidth; /* Why maxWidth? */
+ lastLineParMax += wordExtremes.maxWidth;
+
+ if (word->style->whiteSpace == core::style::WHITE_SPACE_NOWRAP ||
+ word->style->whiteSpace == core::style::WHITE_SPACE_PRE) {
lastLine->parMin += wordExtremes.minWidth + lastSpace;
/* This may also increase the accumulated minimum word width. */
lastLine->maxWordMin =
misc::max (lastLine->maxWordMin, lastLine->parMin);
/* NOTE: Most code relies on that all values of nowrap are equal for all
* words within one line. */
- } else
- /* Simple case. */
+ } else {
lastLine->maxWordMin =
misc::max (lastLine->maxWordMin, wordExtremes.minWidth);
+ }
//DBG_OBJ_SET_NUM(page, "lastLine_par_min", page->lastLine_par_min);
//DBG_OBJ_SET_NUM(page, "lastLine_par_max", page->lastLine_par_max);
@@ -1102,8 +1116,9 @@ void Textblock::wordWrap(int wordIndex)
//DBG_OBJ_ARRSET_NUM (page, "lines.%d.max_word_min", page->num_lines - 1,
// lastLine->max_word_min);
- /* Finally, justify the line. Breaks are ignored, since the HTML
- * parser sometimes assignes the wrong style to them. (TODO: ) */
+ /* Align the line.
+ * \todo Use block's style instead once paragraphs become proper blocks.
+ */
if (word->content.type != core::Content::BREAK) {
switch (word->style->textAlign) {
case core::style::TEXT_ALIGN_LEFT:
@@ -1112,15 +1127,12 @@ void Textblock::wordWrap(int wordIndex)
* future) */
leftOffset = 0;
break;
-
case core::style::TEXT_ALIGN_RIGHT:
leftOffset = availWidth - lastLineWidth;
break;
-
case core::style::TEXT_ALIGN_CENTER:
leftOffset = (availWidth - lastLineWidth) / 2;
break;
-
default:
/* compiler happiness */
leftOffset = 0;
@@ -1130,22 +1142,22 @@ void Textblock::wordWrap(int wordIndex)
if (leftOffset < 0)
leftOffset = 0;
- if (listItem && lastLine == lines->getRef (0)) {
+ if (hasListitemValue && lastLine == lines->getRef (0)) {
/* List item markers are always on the left. */
lastLine->leftOffset = 0;
words->getRef(0)->effSpace = words->getRef(0)->origSpace + leftOffset;
//DBG_OBJ_ARRSET_NUM (page, "words.%d.eff_space", 0,
// page->words[0].eff_space);
- } else
+ } else {
lastLine->leftOffset = leftOffset;
-
+ }
+
int y =
allocation.y - containingBox->allocation.y +
getStyle()->boxOffsetX () + lastLine->top;
lastLine->boxLeft = calcLeftFloatBorder(y, this);
lastLine->boxRight = calcRightFloatBorder(y, this);
}
-
mustQueueResize = true;
//DBG_MSG_END (page);
@@ -1160,6 +1172,7 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
{
core::Requisition requisition;
int availWidth, availAscent, availDescent;
+ core::style::Style *wstyle = widget->getStyle();
/* We ignore line1_offset[_eff]. */
availWidth = this->availWidth - getStyle()->boxDiffWidth () - innerPadding;
@@ -1171,39 +1184,37 @@ void Textblock::calcWidgetSize (core::Widget *widget, core::Requisition *size)
widget->setAscent (availAscent);
widget->setDescent (availDescent);
widget->sizeRequest (size);
- size->ascent -= widget->getStyle()->margin.top;
- size->descent -= widget->getStyle()->margin.bottom;
+// size->ascent -= wstyle->margin.top;
+// size->descent -= wstyle->margin.bottom;
} else {
/* TODO: Use margin.{top|bottom} here, like above.
* (No harm for the next future.) */
- if (widget->getStyle()->width == core::style::LENGTH_AUTO ||
- widget->getStyle()->height == core::style::LENGTH_AUTO)
+ if (wstyle->width == core::style::LENGTH_AUTO ||
+ wstyle->height == core::style::LENGTH_AUTO)
widget->sizeRequest (&requisition);
- if (widget->getStyle()->width == core::style::LENGTH_AUTO)
+ if (wstyle->width == core::style::LENGTH_AUTO)
size->width = requisition.width;
- else if (core::style::isAbsLength (widget->getStyle()->width))
+ else if (core::style::isAbsLength (wstyle->width))
/* Fixed lengths are only applied to the content, so we have to
* add padding, border and margin. */
- size->width = core::style::absLengthVal (widget->getStyle()->width)
- + widget->getStyle()->boxDiffWidth ();
+ size->width = core::style::absLengthVal (wstyle->width)
+ + wstyle->boxDiffWidth ();
else
- size->width =
- (int) (core::style::perLengthVal (widget->getStyle()->width)
- * availWidth);
+ size->width = (int) (core::style::perLengthVal (wstyle->width)
+ * availWidth);
- if (widget->getStyle()->height == core::style::LENGTH_AUTO) {
+ if (wstyle->height == core::style::LENGTH_AUTO) {
size->ascent = requisition.ascent;
size->descent = requisition.descent;
- } else if (core::style::isAbsLength (widget->getStyle()->height)) {
+ } else if (core::style::isAbsLength (wstyle->height)) {
/* Fixed lengths are only applied to the content, so we have to
* add padding, border and margin. */
- size->ascent =
- core::style::absLengthVal (widget->getStyle()->height)
- + widget->getStyle()->boxDiffHeight ();
+ size->ascent = core::style::absLengthVal (wstyle->height)
+ + wstyle->boxDiffHeight ();
size->descent = 0;
} else {
- double len = core::style::perLengthVal (widget->getStyle()->height);
+ double len = core::style::perLengthVal (wstyle->height);
size->ascent = (int) (len * availAscent);
size->descent = (int) (len * availDescent);
}
@@ -1238,8 +1249,7 @@ void Textblock::rewrap ()
//DBG_OBJ_SET_NUM(page, "num_lines", page->num_lines);
//DBG_OBJ_SET_NUM(page, "lastLine_width", page->lastLine_width);
- /* In the word list, we start at the last word, plus one (see definition
- * of last_word), in the line before. */
+ /* In the word list, start at the last word plus one in the line before. */
if (wrapRef > 0) {
/* Note: In this case, Dw_page_real_word_wrap will immediately find
* the need to rewrap the line, since we start with the last one (plus
@@ -1250,11 +1260,11 @@ void Textblock::rewrap ()
lastLineParMin = lastLine->parMin;
lastLineParMax = lastLine->parMax;
- wordIndex = lastLine->lastWord;
- for (i = lastLine->firstWord; i < lastLine->lastWord - 1; i++)
+ wordIndex = lastLine->lastWord + 1;
+ for (i = lastLine->firstWord; i < lastLine->lastWord; i++)
lastLineWidth += (words->getRef(i)->size.width +
words->getRef(i)->origSpace);
- lastLineWidth += words->getRef(lastLine->lastWord - 1)->size.width;
+ lastLineWidth += words->getRef(lastLine->lastWord)->size.width;
} else {
lastLineParMin = 0;
lastLineParMax = 0;
@@ -1298,6 +1308,156 @@ void Textblock::rewrap ()
}
/*
+ * Draw the decorations on a word.
+ */
+void Textblock::decorateText(core::View *view, core::style::Style *style,
+ core::style::Color::Shading shading,
+ int x, int yBase, int width)
+{
+ int y;
+
+ if (style->textDecoration & core::style::TEXT_DECORATION_UNDERLINE) {
+ y = yBase + 1;
+ view->drawLine (style->color, shading, x, y, x + width - 1, y);
+ }
+ if (style->textDecoration & core::style::TEXT_DECORATION_OVERLINE) {
+ y = yBase - style->font->ascent + 1;
+ view->drawLine (style->color, shading, x, y, x + width - 1, y);
+ }
+ if (style->textDecoration & core::style::TEXT_DECORATION_LINE_THROUGH) {
+ int height = 1 + style->font->xHeight / 10;
+
+ y = yBase + (style->font->descent - style->font->ascent) / 2;
+ view->drawRectangle (style->color, shading, true, x, y, width, height);
+ }
+}
+
+/*
+ * Draw a word of text.
+ */
+void Textblock::drawText(int wordIndex, core::View *view,core::Rectangle *area,
+ int xWidget, int yWidgetBase)
+{
+ Word *word = words->getRef(wordIndex);
+ int xWorld = allocation.x + xWidget;
+ core::style::Style *style = word->style;
+ int yWorldBase;
+
+ /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
+ if (style->valign == core::style::VALIGN_SUB)
+ yWidgetBase += style->font->ascent / 3;
+ else if (style->valign == core::style::VALIGN_SUPER) {
+ yWidgetBase -= style->font->ascent / 2;
+ }
+ yWorldBase = yWidgetBase + allocation.y;
+
+ view->drawText (style->font, style->color,
+ core::style::Color::SHADING_NORMAL, xWorld, yWorldBase,
+ word->content.text, strlen (word->content.text));
+
+ if (style->textDecoration)
+ decorateText(view, style, core::style::Color::SHADING_NORMAL, xWorld,
+ yWorldBase, word->size.width);
+
+ for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
+ if (hlStart[layer].index <= wordIndex &&
+ hlEnd[layer].index >= wordIndex) {
+ const int wordLen = strlen (word->content.text);
+ int xStart, width;
+ int firstCharIdx = 0;
+ int lastCharIdx = wordLen;
+
+ if (wordIndex == hlStart[layer].index)
+ firstCharIdx = misc::min (hlStart[layer].nChar, wordLen);
+
+ if (wordIndex == hlEnd[layer].index)
+ lastCharIdx = misc::min (hlEnd[layer].nChar, wordLen);
+
+ xStart = xWorld;
+ if (firstCharIdx)
+ xStart += layout->textWidth (style->font, word->content.text,
+ firstCharIdx);
+ if (firstCharIdx == 0 && lastCharIdx == wordLen)
+ width = word->size.width;
+ else
+ width = layout->textWidth (style->font,
+ word->content.text + firstCharIdx,
+ lastCharIdx - firstCharIdx);
+ if (width > 0) {
+ /* Highlight text */
+ core::style::Color *wordBgColor;
+
+ if (!(wordBgColor = style->backgroundColor))
+ wordBgColor = getBgColor();
+
+ /* Draw background for highlighted text. */
+ view->drawRectangle (
+ wordBgColor, core::style::Color::SHADING_INVERSE, true, xStart,
+ yWorldBase - style->font->ascent, width,
+ style->font->ascent + style->font->descent);
+
+ /* Highlight the text. */
+ view->drawText (style->font, style->color,
+ core::style::Color::SHADING_INVERSE, xStart,
+ yWorldBase, word->content.text + firstCharIdx,
+ lastCharIdx - firstCharIdx);
+
+ if (style->textDecoration)
+ decorateText(view, style, core::style::Color::SHADING_INVERSE,
+ xStart, yWorldBase, width);
+ }
+ }
+ }
+}
+
+/*
+ * Draw a space.
+ */
+void Textblock::drawSpace(int wordIndex, core::View *view,
+ core::Rectangle *area, int xWidget, int yWidgetBase)
+{
+ Word *word = words->getRef(wordIndex);
+ int xWorld = allocation.x + xWidget;
+ int yWorldBase;
+ core::style::Style *style = word->spaceStyle;
+ bool highlight = false;
+
+ /* Adjust the space baseline if it is <SUP>-ed or <SUB>-ed */
+ if (style->valign == core::style::VALIGN_SUB)
+ yWidgetBase += style->font->ascent / 3;
+ else if (style->valign == core::style::VALIGN_SUPER) {
+ yWidgetBase -= style->font->ascent / 2;
+ }
+ yWorldBase = allocation.y + yWidgetBase;
+
+ for (int layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
+ if (hlStart[layer].index <= wordIndex &&
+ hlEnd[layer].index > wordIndex) {
+ highlight = true;
+ break;
+ }
+ }
+ if (highlight) {
+ core::style::Color *spaceBgColor;
+
+ if (!(spaceBgColor = style->backgroundColor))
+ spaceBgColor = getBgColor();
+
+ view->drawRectangle (
+ spaceBgColor, core::style::Color::SHADING_INVERSE, true, xWorld,
+ yWorldBase - style->font->ascent, word->effSpace,
+ style->font->ascent + style->font->descent);
+ }
+ if (style->textDecoration) {
+ core::style::Color::Shading shading = highlight ?
+ core::style::Color::SHADING_INVERSE :
+ core::style::Color::SHADING_NORMAL;
+
+ decorateText(view, style, shading, xWorld, yWorldBase, word->effSpace);
+ }
+}
+
+/*
* Paint a line
* - x and y are toplevel dw coordinates (Question: what Dw? Changed. Test!)
* - area is used always (ev. set it to event->area)
@@ -1305,219 +1465,56 @@ void Textblock::rewrap ()
*/
void Textblock::drawLine (Line *line, core::View *view, core::Rectangle *area)
{
- Word *word;
- int wordIndex;
- int xWidget, yWidget, xWorld, yWorld, yWorldBase;
- int startHL, widthHL;
- int wordLen;
- int diff, effHLStart, effHLEnd, layer;
- core::Widget *child;
- core::Rectangle childArea;
- core::style::Color *color, *thisBgColor, *wordBgColor;
+ int xWidget = lineXOffsetWidget(line);
+ int yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
/* Here's an idea on how to optimize this routine to minimize the number
- * of calls to gdk_draw_string:
+ * of drawing calls:
*
* Copy the text from the words into a buffer, adding a new word
* only if: the attributes match, and the spacing is either zero or
* equal to the width of ' '. In the latter case, copy a " " into
* the buffer. Then draw the buffer. */
- thisBgColor = getBgColor ();
+ for (int wordIndex = line->firstWord;
+ wordIndex <= line->lastWord && xWidget < area->x + area->width;
+ wordIndex++) {
+ Word *word = words->getRef(wordIndex);
- xWidget = lineXOffsetWidget(line);
- xWorld = allocation.x + xWidget;
- yWidget = lineYOffsetWidget (line);
- yWorld = allocation.y + yWidget;
- yWorldBase = yWorld + line->ascent;
+ if (xWidget + word->size.width + word->effSpace >= area->x) {
+ if (word->content.type == core::Content::TEXT ||
+ word->content.type == core::Content::WIDGET) {
- for (wordIndex = line->firstWord; wordIndex < line->lastWord;
- wordIndex++) {
- word = words->getRef(wordIndex);
- diff = 0;
- color = word->style->color;
-
- //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.x", wordIndex,
- // xWidget);
- //DBG_OBJ_ARRSET_NUM (page, "words.%d.<i>drawn at</i>.y", wordIndex,
- // yWidget);
-
- switch (word->content.type) {
- case core::Content::TEXT:
- if (word->style->backgroundColor)
- wordBgColor = word->style->backgroundColor;
- else
- wordBgColor = thisBgColor;
-
- /* Adjust the text baseline if the word is <SUP>-ed or <SUB>-ed. */
- if (word->style->valign == core::style::VALIGN_SUB)
- diff = word->size.ascent / 2;
- else if (word->style->valign == core::style::VALIGN_SUPER)
- diff -= word->size.ascent / 3;
-
- /* Draw background (color, image), when given. */
- if (word->style->hasBackground () && word->size.width > 0)
- drawBox (view, word->style, area,
- xWidget, yWidget + line->ascent - word->size.ascent,
- word->size.width, word->size.ascent + word->size.descent,
- false);
-
- /* Draw space background (color, image), when given. */
- if (word->spaceStyle->hasBackground () && word->effSpace > 0)
- drawBox (view, word->spaceStyle, area,
- xWidget + word->size.width,
- yWidget + line->ascent - word->size.ascent,
- word->effSpace, word->size.ascent + word->size.descent,
- false);
- view->drawText (word->style->font, color,
- core::style::Color::SHADING_NORMAL,
- xWorld, yWorldBase + diff,
- word->content.text, strlen (word->content.text));
-
- /* underline */
- if (word->style->textDecoration &
- core::style::TEXT_DECORATION_UNDERLINE)
- view->drawLine (color, core::style::Color::SHADING_NORMAL,
- xWorld, yWorldBase + 1 + diff,
- xWorld + word->size.width - 1,
- yWorldBase + 1 + diff);
- if (wordIndex + 1 < line->lastWord &&
- (word->spaceStyle->textDecoration
- & core::style::TEXT_DECORATION_UNDERLINE))
- view->drawLine (word->spaceStyle->color,
- core::style::Color::SHADING_NORMAL,
- xWorld + word->size.width,
- yWorldBase + 1 + diff,
- xWorld + word->size.width + word->effSpace - 1,
- yWorldBase + 1 + diff);
-
- /* strike-through */
- if (word->style->textDecoration
- & core::style::TEXT_DECORATION_LINE_THROUGH)
- view->drawLine (color, core::style::Color::SHADING_NORMAL,
- xWorld,
- yWorldBase - word->size.ascent / 2 + diff,
- xWorld + word->size.width - 1,
- yWorldBase - word->size.ascent / 2 + diff);
- if (wordIndex + 1 < line->lastWord &&
- (word->spaceStyle->textDecoration
- & core::style::TEXT_DECORATION_LINE_THROUGH))
- view->drawLine (word->spaceStyle->color,
- core::style::Color::SHADING_NORMAL,
- xWorld + word->size.width,
- yWorldBase - word->size.ascent / 2 + diff,
- xWorld + word->size.width + word->effSpace - 1,
- yWorldBase - word->size.ascent / 2 + diff);
-
- for (layer = 0; layer < core::HIGHLIGHT_NUM_LAYERS; layer++) {
- if (hlStart[layer].index <= wordIndex &&
- hlEnd[layer].index >= wordIndex) {
-
- wordLen = strlen (word->content.text);
- effHLEnd = misc::min (wordLen, hlEnd[layer].nChar);
- effHLStart = 0;
- if (wordIndex == hlStart[layer].index)
- effHLStart = misc::min (hlStart[layer].nChar, wordLen);
-
- effHLEnd = wordLen;
- if (wordIndex == hlEnd[layer].index)
- effHLEnd = misc::min (hlEnd[layer].nChar, wordLen);
-
- startHL = xWorld + layout->textWidth (word->style->font,
- word->content.text,
- effHLStart);
- widthHL =
- layout->textWidth (word->style->font,
- word->content.text + effHLStart,
- effHLEnd - effHLStart);
-
- // If the space after this word highlighted, and this word
- // is not the last one in this line, highlight also the
- // space.
- /** \todo This should also be done with spaces after non-text
- * words, but this is not yet defined very well. */
- if (wordIndex < hlEnd[layer].index &&
- wordIndex < words->size () &&
- wordIndex != line->lastWord - 1)
- widthHL += word->effSpace;
-
-
- if (widthHL != 0) {
- /* Draw background for highlighted text. */
- view->drawRectangle (wordBgColor,
- core::style::Color::SHADING_INVERSE,
- true, startHL,
- yWorldBase - word->size.ascent,
- widthHL,
- word->size.ascent + word->size.descent);
-
- /* Highlight the text. */
- view->drawText (word->style->font,
- color, core::style::Color::SHADING_INVERSE,
- startHL, yWorldBase + diff,
- word->content.text + effHLStart,
- effHLEnd - effHLStart);
-
- /* underline and strike-through */
- if (word->style->textDecoration
- & core::style::TEXT_DECORATION_UNDERLINE)
- view->drawLine (color,
- core::style::Color::SHADING_INVERSE,
- startHL, yWorldBase + 1 + diff,
- startHL + widthHL - 1,
- yWorldBase + 1 + diff);
- if (word->style->textDecoration
- & core::style::TEXT_DECORATION_LINE_THROUGH)
- view->drawLine (color,
- core::style::Color::SHADING_INVERSE,
- startHL,
- yWorldBase - word->size.ascent / 2 + diff,
- startHL + widthHL - 1,
- yWorldBase - word->size.ascent / 2
- + diff);
+ if (word->size.width > 0) {
+ if (word->style->hasBackground ()) {
+ drawBox (view, word->style, area, xWidget,
+ yWidgetBase - line->boxAscent, word->size.width,
+ line->boxAscent + line->boxDescent, false);
}
- }
- }
- break;
-
- case core::Content::WIDGET:
- child = word->content.widget;
- if (child->intersects (area, &childArea))
- child->draw (view, &childArea);
- break;
+ if (word->content.type == core::Content::WIDGET) {
+ core::Widget *child = word->content.widget;
+ core::Rectangle childArea;
- case core::Content::ANCHOR: case core::Content::BREAK:
- /* nothing - an anchor/break isn't seen */
- /*
- * Historical note:
- * > BUG: sometimes anchors have x_space;
- * > we subtract that just in case --EG
- * This is inconsistent with other parts of the code, so it should
- * be tried to prevent this earlier.--SG
- */
- /*
- * x_viewport -= word->size.width + word->eff_space;
- * xWidget -= word->size.width + word->eff_space;
- */
-#if 0
- /* Useful for testing: draw breaks. */
- if (word->content.type == DW_CONTENT_BREAK)
- gdk_draw_rectangle (window, color, TRUE,
- p_Dw_widget_xWorld_to_viewport (widget,
- widget->allocation.x +
- Dw_page_line_total_x_offset(page, line)),
- y_viewport_base + line->descent,
- DW_WIDGET_CONTENT_WIDTH(widget),
- word->content.break_space);
-#endif
- break;
+ if (child->intersects (area, &childArea))
+ child->draw (view, &childArea);
+ } else {
+ drawText(wordIndex, view, area, xWidget, yWidgetBase);
+ }
+ }
+ if (word->effSpace > 0 && wordIndex < line->lastWord &&
+ words->getRef(wordIndex + 1)->content.type !=
+ core::Content::BREAK) {
+ if (word->spaceStyle->hasBackground ())
+ drawBox (view, word->spaceStyle, area,
+ xWidget + word->size.width,
+ yWidgetBase - line->boxAscent, word->effSpace,
+ line->boxAscent + line->boxDescent, false);
+ drawSpace(wordIndex, view, area, xWidget + word->size.width,
+ yWidgetBase);
+ }
- default:
- fprintf (stderr, "BUG!!! at (%d, %d).\n", xWorld, yWorldBase + diff);
- break;
+ }
}
-
- xWorld += word->size.width + word->effSpace;
xWidget += word->size.width + word->effSpace;
}
}
@@ -1534,19 +1531,19 @@ int Textblock::findLineIndex (int y)
while ( step > 1 ) {
index = low + step;
if (index <= maxIndex &&
- lineYOffsetWidgetI (index) < y)
+ lineYOffsetWidgetI (index) <= y)
low = index;
step = (step + 1) >> 1;
}
- if (low < maxIndex && lineYOffsetWidgetI (low + 1) < y)
+ if (low < maxIndex && lineYOffsetWidgetI (low + 1) <= y)
low++;
/*
* This new routine returns the line number between (top) and
- * (top + size.ascent + size.descent + break_space): the space
+ * (top + size.ascent + size.descent + breakSpace): the space
* _below_ the line is considered part of the line. Old routine
- * returned line number between (top - previous_line->break_space)
+ * returned line number between (top - previous_line->breakSpace)
* and (top + size.ascent + size.descent): the space _above_ the
* line was considered part of the line. This is important for
* Dw_page_find_link() --EG
@@ -1562,13 +1559,13 @@ int Textblock::findLineOfWord (int wordIndex)
{
int high = lines->size () - 1, index, low = 0;
- //g_return_val_if_fail (word_index >= 0, -1);
- //g_return_val_if_fail (word_index < page->num_words, -1);
+ if (wordIndex < 0 || wordIndex >= words->size ())
+ return -1;
while (true) {
index = (low + high) / 2;
if (wordIndex >= lines->getRef(index)->firstWord) {
- if (wordIndex < lines->getRef(index)->lastWord)
+ if (wordIndex <= lines->getRef(index)->lastWord)
return index;
else
low = index + 1;
@@ -1580,29 +1577,36 @@ int Textblock::findLineOfWord (int wordIndex)
/**
* \brief Find the index of the word, or -1.
*/
-int Textblock::findWord (int x, int y)
+Textblock::Word *Textblock::findWord (int x, int y, bool *inSpace)
{
int lineIndex, wordIndex;
- int xCursor, lastXCursor;
+ int xCursor, lastXCursor, yWidgetBase;
Line *line;
Word *word;
+ *inSpace = false;
+
if ((lineIndex = findLineIndex (y)) >= lines->size ())
- return -1;
+ return NULL;
line = lines->getRef (lineIndex);
- if (lineYOffsetWidget (line) + line->ascent + line->descent <= y)
- return -1;
+ yWidgetBase = lineYOffsetWidget (line) + line->boxAscent;
+ if (yWidgetBase + line->boxDescent <= y)
+ return NULL;
xCursor = lineXOffsetWidget (line);
- for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) {
+ for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
word = words->getRef (wordIndex);
lastXCursor = xCursor;
xCursor += word->size.width + word->effSpace;
- if (lastXCursor <= x && xCursor > x)
- return wordIndex;
+ if (lastXCursor <= x && xCursor > x &&
+ y > yWidgetBase - word->size.ascent &&
+ y <= yWidgetBase + word->size.descent) {
+ *inSpace = x >= xCursor - word->effSpace;
+ return word;
+ }
}
- return -1;
+ return NULL;
}
void Textblock::draw (core::View *view, core::Rectangle *area)
@@ -1670,37 +1674,67 @@ Textblock::Word *Textblock::addWord (int width, int ascent, int descent,
/**
* Calculate the size of a text word.
*/
-void Textblock::calcTextSize (const char *text, core::style::Style *style,
+void Textblock::calcTextSize (const char *text, size_t len,
+ core::style::Style *style,
core::Requisition *size)
{
- size->width =
- layout->textWidth (style->font, text, strlen (text));
+ size->width = layout->textWidth (style->font, text, len);
size->ascent = style->font->ascent;
size->descent = style->font->descent;
+ /*
+ * For 'normal' line height, just use ascent and descent from font.
+ * For absolute/percentage, line height is relative to font size, which
+ * is (irritatingly) smaller than ascent+descent.
+ */
+ if (style->lineHeight != core::style::LENGTH_AUTO) {
+ int height, leading;
+ float factor = style->font->size;
+
+ factor /= (style->font->ascent + style->font->descent);
+
+ size->ascent = size->ascent * factor + 0.5;
+ size->descent = size->descent * factor + 0.5;
+
+ /* TODO: The containing block's line-height property gives a minimum
+ * height for the line boxes. (Even when it's set to 'normal', i.e.,
+ * AUTO? Apparently.) Once all block elements make Textblocks or
+ * something, this can be handled.
+ */
+ if (core::style::isAbsLength (style->lineHeight))
+ height = core::style::absLengthVal(style->lineHeight);
+ else
+ height = core::style::perLengthVal(style->lineHeight) *
+ style->font->size;
+ leading = height - style->font->size;
+
+ size->ascent += leading / 2;
+ size->descent += leading - (leading / 2);
+ }
+
/* In case of a sub or super script we increase the word's height and
* potentially the line's height.
*/
if (style->valign == core::style::VALIGN_SUB)
- size->descent += (size->ascent / 2);
+ size->descent += (style->font->ascent / 3);
else if (style->valign == core::style::VALIGN_SUPER)
- size->ascent += (size->ascent / 3);
+ size->ascent += (style->font->ascent / 2);
}
/**
- * Add a word to the page structure. Stashes the argument pointer in
- * the page data structure so that it will be deallocated on destroy.
+ * Add a word to the page structure.
*/
-void Textblock::addText (const char *text, core::style::Style *style)
+void Textblock::addText (const char *text, size_t len,
+ core::style::Style *style)
{
Word *word;
core::Requisition size;
- calcTextSize (text, style, &size);
+ calcTextSize (text, len, style, &size);
word = addWord (size.width, size.ascent, size.descent, style);
word->content.type = core::Content::TEXT;
- word->content.text = layout->textZone->strdup(text);
+ word->content.text = layout->textZone->strndup(text, len);
//DBG_OBJ_ARRSET_STR (page, "words.%d.content.text", page->num_words - 1,
// word->content.text);
@@ -1717,7 +1751,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
core::Requisition size;
/* We first assign -1 as parent_ref, since the call of widget->size_request
- * will otherwise let this DwPage be rewrapped from the beginning.
+ * will otherwise let this Textblock be rewrapped from the beginning.
* (parent_ref is actually undefined, but likely has the value 0.) At the,
* end of this function, the correct value is assigned. */
widget->parentRef = -1;
@@ -1749,7 +1783,7 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
/**
- * Add an anchor to the page. "name" is copied, so no strdup is neccessary for
+ * Add an anchor to the page. "name" is copied, so no strdup is necessary for
* the caller.
*
* Return true on success, and false, when this anchor had already been
@@ -1757,11 +1791,10 @@ void Textblock::addWidget (core::Widget *widget, core::style::Style *style)
*/
bool Textblock::addAnchor (const char *name, core::style::Style *style)
{
- Word *word;
char *copy;
int y;
- // Since an anchor does not take any space, it is safe to call
+ // Since an anchor does not take any space, it is safe to call
// addAnchor already here.
if (wasAllocated ()) {
if (lines->size () == 0)
@@ -1774,15 +1807,17 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style)
if (copy == NULL)
/**
- * \todo It may be neccessary for future uses to save the anchor in
+ * \todo It may be necessary for future uses to save the anchor in
* some way, e.g. when parts of the widget tree change.
*/
return false;
else {
- word = addWord (0, 0, 0, style);
- word->content.type = core::Content::ANCHOR;
- word->content.anchor = copy;
- wordWrap (words->size () - 1);
+ Anchor *anchor;
+
+ anchors->increase();
+ anchor = anchors->getRef(anchors->size() - 1);
+ anchor->name = copy;
+ anchor->wordIndex = words->size();
return true;
}
}
@@ -1793,22 +1828,15 @@ bool Textblock::addAnchor (const char *name, core::style::Style *style)
*/
void Textblock::addSpace (core::style::Style *style)
{
- int nl, nw;
- int space;
+ int wordIndex = words->size () - 1;
- nl = lines->size () - 1;
- if (nl >= 0) {
- nw = words->size () - 1;
- if (nw >= 0) {
- /* TODO: remove this test case */
- //if (page->words[nw].orig_space != 0) {
- // _MSG(" a_Dw_page_add_space:: already existing space!!!\n");
- //}
+ if (wordIndex >= 0) {
+ Word *word = words->getRef(wordIndex);
- space = style->font->spaceWidth;
- words->getRef(nw)->origSpace = space;
- words->getRef(nw)->effSpace = space;
- words->getRef(nw)->content.space = true;
+ if (!word->content.space) {
+ word->content.space = true;
+ word->effSpace = word->origSpace = style->font->spaceWidth +
+ style->wordSpacing;
//DBG_OBJ_ARRSET_NUM (page, "words.%d.orig_space", nw,
// page->words[nw].orig_space);
@@ -1816,9 +1844,8 @@ void Textblock::addSpace (core::style::Style *style)
// page->words[nw].eff_space);
//DBG_OBJ_ARRSET_NUM (page, "words.%d.content.space", nw,
// page->words[nw].content.space);
-
- words->getRef(nw)->spaceStyle->unref ();
- words->getRef(nw)->spaceStyle = style;
+ word->spaceStyle->unref ();
+ word->spaceStyle = style;
style->ref ();
}
}
@@ -1830,45 +1857,41 @@ void Textblock::addSpace (core::style::Style *style)
*/
void Textblock::addParbreak (int space, core::style::Style *style)
{
- Word *word, *word2 = NULL; // Latter for compiler happiness, search!
- bool isfirst;
- Widget *widget;
- int lineno;
+ Word *word;
/* A break may not be the first word of a page, or directly after
the bullet/number (which is the first word) in a list item. (See
also comment in Dw_page_size_request.) */
if (words->size () == 0 ||
- (listItem && words->size () == 1)) {
+ (hasListitemValue && words->size () == 1)) {
/* This is a bit hackish: If a break is added as the
first/second word of a page, and the parent widget is also a
- DwPage, and there is a break before -- this is the case when
+ Textblock, and there is a break before -- this is the case when
a widget is used as a text box (lists, blockquotes, list
items etc) -- then we simply adjust the break before, in a
way that the space is in any case visible. */
+ Widget *widget;
- /* Find the widget where to adjust the break_space. */
+ /* Find the widget where to adjust the breakSpace. */
for (widget = this;
widget->getParent() &&
widget->getParent()->instanceOf (Textblock::CLASS_ID);
widget = widget->getParent ()) {
Textblock *textblock2 = (Textblock*)widget->getParent ();
- if (textblock2->listItem)
- isfirst = (textblock2->words->get(1).content.type
- == core::Content::WIDGET
- && textblock2->words->get(1).content.widget == widget);
- else
- isfirst = (textblock2->words->get(0).content.type
- == core::Content::WIDGET
- && textblock2->words->get(0).content.widget == widget);
+ int index = textblock2->hasListitemValue ? 1 : 0;
+ bool isfirst = (textblock2->words->getRef(index)->content.type
+ == core::Content::WIDGET
+ && textblock2->words->getRef(index)->content.widget
+ == widget);
if (!isfirst) {
/* The page we searched for has been found. */
+ Word *word2;
// ABC
- lineno = widget->parentRef >> 3;
+ int lineno = widget->parentRef >> 3;
if (lineno > 0 &&
(word2 =
textblock2->words->getRef(textblock2->lines
- ->get(lineno - 1).firstWord)) &&
+ ->getRef(lineno - 1)->firstWord)) &&
word2->content.type == core::Content::BREAK) {
if (word2->content.breakSpace < space) {
word2->content.breakSpace = space;
@@ -1897,7 +1920,7 @@ void Textblock::addParbreak (int space, core::style::Style *style)
misc::max (word->content.breakSpace, space);
lastLine->breakSpace =
misc::max (word->content.breakSpace,
- lastLine->marginDescent - lastLine->descent,
+ lastLine->marginDescent - lastLine->boxDescent,
lastLine->breakSpace);
return;
}
@@ -1916,7 +1939,7 @@ void Textblock::addLinebreak (core::style::Style *style)
Word *word;
if (words->size () == 0 ||
- words->get(words->size () - 1).content.type == core::Content::BREAK)
+ words->getRef(words->size () - 1)->content.type == core::Content::BREAK)
// An <BR> in an empty line gets the height of the current font
// (why would someone else place it here?), ...
word = addWord (0, style->font->ascent, style->font->descent, style);
@@ -1926,7 +1949,6 @@ void Textblock::addLinebreak (core::style::Style *style)
word->content.type = core::Content::BREAK;
word->content.breakSpace = 0;
- word->style = style;
wordWrap (words->size () - 1);
}
@@ -1949,9 +1971,9 @@ void Textblock::addFloatIntoGenerator (core::Widget *widget, core::style::Style
/**
* \brief Search recursively through widget.
- *
+ *
* This is an optimized version of the general
- * dw::core::Widget::getWidgetAtPoint method.
+ * dw::core::Widget::getWidgetAtPoint method.
*/
core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
{
@@ -1973,7 +1995,7 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
line = lines->getRef (lineIndex);
- for (wordIndex = line->firstWord; wordIndex < line->lastWord; wordIndex++) {
+ for (wordIndex = line->firstWord; wordIndex <= line->lastWord;wordIndex++) {
Word *word = words->getRef (wordIndex);
if (word->content.type == core::Content::WIDGET) {
@@ -1996,19 +2018,16 @@ core::Widget *Textblock::getWidgetAtPoint(int x, int y, int level)
*/
void Textblock::handOverBreak (core::style::Style *style)
{
- #if 0
- MISSING
- DwPageLine *last_line;
- DwWidget *parent;
-
- if (page->num_lines == 0)
- return;
+ if (lines->size() > 0) {
+ Widget *parent;
+ Line *lastLine = lines->getRef (lines->size () - 1);
- last_line = &page->lines[page->num_lines - 1];
- if (last_line->break_space != 0 &&
- (parent = DW_WIDGET(page)->parent) && DW_IS_PAGE (parent))
- a_Dw_page_add_parbreak (DW_PAGE (parent), last_line->break_space, style);
-#endif
+ if (lastLine->breakSpace != 0 && (parent = getParent()) &&
+ parent->instanceOf (Textblock::CLASS_ID)) {
+ Textblock *textblock2 = (Textblock*) parent;
+ textblock2->addParbreak(lastLine->breakSpace, style);
+ }
+ }
}
/*
@@ -2039,10 +2058,10 @@ void Textblock::changeLinkColor (int link, int newColor)
for (int lineIndex = 0; lineIndex < lines->size(); lineIndex++) {
bool changed = false;
Line *line = lines->getRef (lineIndex);
- int wordIndex;
+ int wordIdx;
- for (wordIndex = line->firstWord;wordIndex < line->lastWord;wordIndex++){
- Word *word = words->getRef(wordIndex);
+ for (wordIdx = line->firstWord; wordIdx <= line->lastWord; wordIdx++){
+ Word *word = words->getRef(wordIdx);
if (word->style->x_link == link) {
core::style::StyleAttrs styleAttrs;
@@ -2051,14 +2070,14 @@ void Textblock::changeLinkColor (int link, int newColor)
case core::Content::TEXT:
{ core::style::Style *old_style = word->style;
styleAttrs = *old_style;
- styleAttrs.color = core::style::Color::createSimple (layout,
- newColor);
+ styleAttrs.color = core::style::Color::create (layout,
+ newColor);
word->style = core::style::Style::create (layout, &styleAttrs);
old_style->unref();
old_style = word->spaceStyle;
styleAttrs = *old_style;
- styleAttrs.color = core::style::Color::createSimple (layout,
- newColor);
+ styleAttrs.color = core::style::Color::create (layout,
+ newColor);
word->spaceStyle =
core::style::Style::create(layout, &styleAttrs);
old_style->unref();
@@ -2067,10 +2086,10 @@ void Textblock::changeLinkColor (int link, int newColor)
case core::Content::WIDGET:
{ core::Widget *widget = word->content.widget;
styleAttrs = *widget->getStyle();
- styleAttrs.color = core::style::Color::createSimple (layout,
- newColor);
+ styleAttrs.color = core::style::Color::create (layout,
+ newColor);
styleAttrs.setBorderColor(
- core::style::Color::createShaded(layout, newColor));
+ core::style::Color::create (layout, newColor));
widget->setStyle(
core::style::Style::create (layout, &styleAttrs));
break;
@@ -2083,7 +2102,7 @@ void Textblock::changeLinkColor (int link, int newColor)
}
if (changed)
queueDrawArea (0, lineYOffsetWidget(line), allocation.width,
- line->ascent + line->descent);
+ line->boxAscent + line->boxDescent);
}
}
@@ -2325,7 +2344,7 @@ Textblock::TextblockIterator::TextblockIterator (Textblock *textblock,
else if (index >= textblock->words->size ())
content.type = core::Content::END;
else
- content = textblock->words->get(index).content;
+ content = textblock->words->getRef(index)->content;
}
object::Object *Textblock::TextblockIterator::clone()
@@ -2337,23 +2356,23 @@ int Textblock::TextblockIterator::compareTo(misc::Comparable *other)
{
return index - ((TextblockIterator*)other)->index;
}
-
+
bool Textblock::TextblockIterator::next ()
{
Textblock *textblock = (Textblock*)getWidget();
if (content.type == core::Content::END)
return false;
-
+
do {
index++;
if (index >= textblock->words->size ()) {
content.type = core::Content::END;
return false;
}
- } while ((textblock->words->get(index).content.type & getMask()) == 0);
+ } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
- content = textblock->words->get(index).content;
+ content = textblock->words->getRef(index)->content;
return true;
}
@@ -2363,16 +2382,16 @@ bool Textblock::TextblockIterator::prev ()
if (content.type == core::Content::START)
return false;
-
+
do {
index--;
if (index < 0) {
content.type = core::Content::START;
return false;
}
- } while ((textblock->words->get(index).content.type & getMask()) == 0);
+ } while ((textblock->words->getRef(index)->content.type & getMask()) == 0);
- content = textblock->words->get(index).content;
+ content = textblock->words->getRef(index)->content;
return true;
}
@@ -2440,16 +2459,19 @@ void Textblock::queueDrawRange (int index1, int index2)
to = misc::min (to, words->size () - 1);
to = misc::max (to, 0);
- int line1 = findLineOfWord (from);
- int line2 = findLineOfWord (to);
+ int line1idx = findLineOfWord (from);
+ int line2idx = findLineOfWord (to);
+
+ if (line1idx >= 0 && line2idx >= 0) {
+ Line *line1 = lines->getRef (line1idx),
+ *line2 = lines->getRef (line2idx);
+ int y = lineYOffsetWidget (line1) + line1->boxAscent -
+ line1->contentAscent;
+ int h = lineYOffsetWidget (line2) + line2->boxAscent +
+ line2->contentDescent - y;
- queueDrawArea (0,
- lineYOffsetWidgetI (line1),
- allocation.width,
- lineYOffsetWidgetI (line2)
- - lineYOffsetWidgetI (line1)
- + lines->getRef (line2)->ascent
- + lines->getRef (line2)->descent);
+ queueDrawArea (0, y, allocation.width, h);
+ }
}
void Textblock::TextblockIterator::getAllocation (int start, int end,
@@ -2462,13 +2484,31 @@ void Textblock::TextblockIterator::getAllocation (int start, int end,
allocation->x =
textblock->allocation.x + textblock->lineXOffsetWidget (line);
- for (int i = line->firstWord; i < index; i++)
- allocation->x += textblock->words->getRef(i)->size.width;
- allocation->y =
- textblock->allocation.y
- + textblock->lineYOffsetWidget (line) + line->ascent - word->size.ascent;
+ for (int i = line->firstWord; i < index; i++) {
+ Word *w = textblock->words->getRef(i);
+ allocation->x += w->size.width + w->effSpace;
+ }
+ if (start > 0 && word->content.type == core::Content::TEXT) {
+ allocation->x += textblock->layout->textWidth (word->style->font,
+ word->content.text,
+ start);
+ }
+ allocation->y = textblock->lineYOffsetCanvas (line) + line->boxAscent -
+ word->size.ascent;
+
allocation->width = word->size.width;
+ if (word->content.type == core::Content::TEXT) {
+ int wordEnd = strlen(word->content.text);
+
+ if (start > 0 || end < wordEnd) {
+ end = misc::min(end, wordEnd); /* end could be INT_MAX */
+ allocation->width =
+ textblock->layout->textWidth (word->style->font,
+ word->content.text + start,
+ end - start);
+ }
+ }
allocation->ascent = word->size.ascent;
allocation->descent = word->size.descent;
}
diff --git a/dw/textblock.hh b/dw/textblock.hh
index 80e16393..15f81eb1 100644
--- a/dw/textblock.hh
+++ b/dw/textblock.hh
@@ -6,8 +6,6 @@
namespace dw {
-using namespace lout;
-
/**
* \brief A Widget for rendering text blocks, i.e. paragraphs or sequences
* of paragraphs.
@@ -16,118 +14,118 @@ using namespace lout;
* floats have been implementet. Will be updated and extended soon.
*
* <h3>Signals</h3>
- *
+ *
* dw::Textblock uses the signals defined in
- * dw::core::Widget::LinkReceiver, related to links. The coordinates are
+ * dw::core::Layout::LinkReceiver, related to links. The coordinates are
* always -1.
- *
- *
+ *
+ *
* <h3>Collapsing Spaces</h3>
- *
+ *
* The idea behind this is that every paragraph has a specific vertical
* space around and that they are combined to one space, according to
* rules stated below. A paragraph consists either of the lines between
* two paragraph breaks within a dw::Textblock, or of a dw::Textblock
* within a dw::Textblock, in a single line; the latter is used for
* indented boxes and list items.
- *
+ *
* The rules:
- *
+ *
* <ol>
* <li> If a paragraph is following by another, the space between them is the
* maximum of both box spaces:
- *
+ *
* \image html dw-textblock-collapsing-spaces-1-1.png
- *
+ *
* are combined like this:
- *
+ *
* \image html dw-textblock-collapsing-spaces-1-2.png
- *
+ *
* <li> a) If one paragraph is the first paragraph within another, the upper
* space of these paragraphs collapse. b) The analogue is the case for the
* last box:
- *
+ *
* \image html dw-textblock-collapsing-spaces-2-1.png
- *
+ *
* If B and C are put into A, the result is:
- *
+ *
* \image html dw-textblock-collapsing-spaces-2-2.png
* </ol>
- *
+ *
* For achieving this, there are some features of dw::Textblock:
- *
+ *
* <ul>
* <li> Consequent breaks are automatically combined, according to
* rule 1. See the code of dw::Textblock::addParBreak for details.
- *
+ *
* <li> If a break is added as the first word of the dw::Textblock within
* another dw::Textblock, collapsing according to rule 2a is done
* automatically. See the code of dw::Textblock::addParBreak.
- *
+ *
* <li> To collapse spaces according to rule 2b,
* dw::Textblock::addParBreak::handOverBreak must be called for
* the \em inner widget. The HTML parser does this in
* Html_eventually_pop_dw.
* </ul>
- *
- *
+ *
+ *
* <h3>Collapsing Margins</h3>
- *
+ *
* Collapsing margins, as defined in the CSS2 specification, are,
* supported in addition to collapsing spaces. Also, spaces and margins
* collapse themselves. I.e., the space between two paragraphs is the
* maximum of the space calculated as described in "Collapsing Spaces"
* and the space calculated according to the rules for collapsing margins.
- *
+ *
* (This is an intermediate hybrid state, collapsing spaces are used in
* the current version of dillo, while I implemented collapsing margins
* for the CSS prototype and integrated it already into the main trunk. For
* a pure CSS-based dillo, collapsing spaces will not be needed anymore, and
* may be removed for simplicity.)
- *
- *
+ *
+ *
* <h3>Some Internals</h3>
- *
- * There are two lists, dw::Textblock::words and
- * dw::Textblock::lines. The word list is quite static; only new words
- * may be added. A word is either text, a widget, a break or an
- * anchor. Anchors are stored in the text, because it may be necessary to
- * correct the scroller positions at rewrapping.
- *
- * Lines refer to the word list (first and last), they are completely
+ *
+ * There are 3 lists, dw::Textblock::words, dw::Textblock::lines, and
+ * dw::Textblock::anchors. The word list is quite static; only new words
+ * may be added. A word is either text, a widget, or a break.
+ *
+ * Lines refer to the word list (first and last). They are completely
* redundant, i.e., they can be rebuilt from the words. Lines can be
* rewrapped either completely or partially (see "Incremental Resizing"
* below). For the latter purpose, several values are accumulated in the
* lines. See dw::Textblock::Line for details.
- *
- *
+ *
+ * Anchors associate the anchor name with the index of the next word at
+ * the point of the anchor.
+ *
* <h4>Incremental Resizing</h4>
- *
+ *
* dw::Textblock makes use of incremental resizing as described in \ref
* dw-widget-sizes. The parentRef is, for children of a dw::Textblock, simply
* the number of the line.
- *
+ *
* Generally, there are three cases which may change the size of the
* widget:
- *
+ *
* <ul>
* <li> The available size of the widget has changed, e.g., because the
* user has changed the size of the browser window. In this case,
* it is necessary to rewrap all the lines.
- *
+ *
* <li> A child widget has changed its size. In this case, only a rewrap
* down from the line where this widget is located is necessary.
- *
+ *
* (This case is very important for tables. Tables are quite at the
* bottom, so that a partial rewrap is relevant. Otherwise, tables
* change their size quite often, so that this is necessary for a
* fast, non-blocking rendering)
- *
+ *
* <li> A word (or widget, break etc.) is added to the text block. This
* makes it possible to reuse the old size by simply adjusting the
* current width and height, so no rewrapping is necessary.
* </ul>
- *
+ *
* The state of the size calculation is stored in wrapRef within
* dw::Textblock, which has the value -1 if no rewrapping of lines
* necessary, or otherwise the line from which a rewrap is necessary.
@@ -141,7 +139,7 @@ private:
class FloatSide
{
protected:
- class Float: public object::Object
+ class Float: public lout::object::Object
{
public:
Textblock *floatGenerator;
@@ -150,8 +148,8 @@ private:
};
Textblock *floatContainer;
- container::typed::Vector<Float> *floats;
- container::typed::HashTable<object::TypedPointer<dw::core::Widget>, Float> *floatsByWidget;
+ lout::container::typed::Vector<Float> *floats;
+ lout::container::typed::HashTable<lout::object::TypedPointer<dw::core::Widget>, Float> *floatsByWidget;
Float *findFloat(int y);
@@ -198,13 +196,13 @@ private:
protected:
struct Line
{
- int firstWord; /* first-word's position in DwPageWord [0 based] */
- int lastWord; /* last-word's position in DwPageWord [1 based] */
+ int firstWord; /* first word's index in word vector */
+ int lastWord; /* last word's index in word vector */
/* "top" is always relative to the top of the first line, i.e.
* page->lines[0].top is always 0. */
- int top, ascent, descent, breakSpace;
- int leftOffset; /* nonzero for centered and rightly-aligned text */
+ int top, boxAscent, boxDescent, contentAscent, contentDescent,
+ breakSpace, leftOffset;
int boxLeft, boxRight;
/* This is similar to descent, but includes the bottom margins of the
@@ -229,9 +227,9 @@ protected:
/* TODO: perhaps add a xLeft? */
core::Requisition size;
/* Space after the word, only if it's not a break: */
- unsigned short origSpace; /* from font, set by addSpace */
- unsigned short effSpace; /* effective space, set by wordWrap,
- * used for drawing etc. */
+ short origSpace; /* from font, set by addSpace */
+ short effSpace; /* effective space, set by wordWrap,
+ * used for drawing etc. */
core::Content content;
core::style::Style *style;
@@ -239,6 +237,12 @@ protected:
later set by a_Dw_page_add_space */
};
+ struct Anchor
+ {
+ char *name;
+ int wordIndex;
+ };
+
class TextblockIterator: public core::Iterator
{
private:
@@ -250,8 +254,8 @@ protected:
TextblockIterator (Textblock *textblock, core::Content::Type mask,
int index);
- object::Object *clone();
- int compareTo(misc::Comparable *other);
+ lout::object::Object *clone();
+ int compareTo(lout::misc::Comparable *other);
bool next ();
bool prev ();
@@ -263,7 +267,7 @@ protected:
friend class TextblockIterator;
/* These fields provide some ad-hoc-functionality, used by sub-classes. */
- bool listItem; /* If true, the first word of the page is treated
+ bool hasListitemValue; /* If true, the first word of the page is treated
specially (search in source). */
int innerPadding; /* This is an additional padding on the left side
(used by ListItem). */
@@ -304,17 +308,13 @@ protected:
int lastLineParMax;
int wrapRef; /* [0 based] */
- misc::SimpleVector <Line> *lines;
- misc::SimpleVector <Word> *words;
+ lout::misc::SimpleVector <Line> *lines;
+ lout::misc::SimpleVector <Word> *words;
+ lout::misc::SimpleVector <Anchor> *anchors;
struct {int index, nChar;}
hlStart[core::HIGHLIGHT_NUM_LAYERS], hlEnd[core::HIGHLIGHT_NUM_LAYERS];
- /* The word index of the link under a button press, and the char
- * position */
- int linkPressedIndex;
- int link_pressedCharPos;
-
int hoverLink; /* The link under the button. */
core::style::Tooltip *hoverTooltip; /* The tooltip under the button. No ref
* hold. */
@@ -324,17 +324,24 @@ protected:
void getWordExtremes (Word *word, core::Extremes *extremes);
void markChange (int ref);
void justifyLine (Line *line, int availWidth);
- void addLine (int wordInd, bool newPar);
+ Line *addLine (int wordInd, bool newPar);
void calcWidgetSize (core::Widget *widget, core::Requisition *size);
void rewrap ();
+ void decorateText(core::View *view, core::style::Style *style,
+ core::style::Color::Shading shading,
+ int x, int yBase, int width);
+ void drawText(int wordIndex, core::View *view, core::Rectangle *area,
+ int xWidget, int yWidgetBase);
+ void drawSpace(int wordIndex, core::View *view, core::Rectangle *area,
+ int xWidget, int yWidgetBase);
void drawLine (Line *line, core::View *view, core::Rectangle *area);
int findLineIndex (int y);
int findLineOfWord (int wordIndex);
- int findWord (int x, int y);
+ Word *findWord (int x, int y, bool *inSpace);
Word *addWord (int width, int ascent, int descent,
core::style::Style *style);
- void calcTextSize (const char *text, core::style::Style *style,
+ void calcTextSize (const char *text, size_t len, core::style::Style *style,
core::Requisition *size);
void addFloatIntoContainer(core::Widget *widget, Textblock *floatGenerator);
@@ -355,7 +362,7 @@ protected:
*/
inline int lineXOffsetContents (Line *line)
{
- return innerPadding + line->leftOffset + line->boxLeft +
+ return innerPadding + line->leftOffset + line->boxLeft +
(line == lines->getRef (0) ? line1OffsetEff : 0);
}
@@ -371,7 +378,7 @@ protected:
inline int lineYOffsetWidgetAllocation (Line *line,
core::Allocation *allocation)
{
- return line->top + (allocation->ascent - lines->getRef(0)->ascent);
+ return line->top + (allocation->ascent - lines->getRef(0)->boxAscent);
}
inline int lineYOffsetWidget (Line *line)
@@ -400,7 +407,7 @@ protected:
{
return lineYOffsetWidget (lines->getRef (lineIndex));
}
-
+
inline int lineYOffsetCanvasI (int lineIndex)
{
return lineYOffsetCanvas (lines->getRef (lineIndex));
@@ -423,7 +430,7 @@ protected:
void setWidth (int width);
void setAscent (int ascent);
void setDescent (int descent);
- void draw (core::View *view, core::Rectangle *area);
+ void draw (core::View *view, core::Rectangle *area);
bool buttonPressImpl (core::EventButton *event);
bool buttonReleaseImpl (core::EventButton *event);
@@ -439,11 +446,15 @@ public:
Textblock(bool limitTextWidth);
~Textblock();
- core::Iterator *iterator (core::Content::Type mask, bool atEnd);
+ core::Iterator *iterator (core::Content::Type mask, bool atEnd);
void flush ();
- void addText (const char *text, core::style::Style *style);
+ void addText (const char *text, size_t len, core::style::Style *style);
+ inline void addText (const char *text, core::style::Style *style)
+ {
+ addText (text, strlen(text), style);
+ }
void addWidget (core::Widget *widget, core::style::Style *style);
bool addAnchor (const char *name, core::style::Style *style);
void addSpace(core::style::Style *style);
diff --git a/dw/types.cc b/dw/types.cc
index 94f95b42..672ec7f6 100644
--- a/dw/types.cc
+++ b/dw/types.cc
@@ -1,3 +1,7 @@
+// Rectangle::intersectsWith() has code that was derived from gdkrectangle.c.
+// gdkrectangle.c bears the notice that GDK, the GIMP Drawing Kit, is
+// "Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald".
+
/*
* Dillo Widget
*
@@ -14,13 +18,15 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core.hh"
+#include "../lout/msg.h"
+
+using namespace lout;
namespace dw {
namespace core {
@@ -33,12 +39,20 @@ Rectangle::Rectangle (int x, int y, int width, int height)
this->height = height;
}
+/*
+ * Draw rectangle in view relative to point (x,y).
+ */
+void Rectangle::draw (core::View *view, core::style::Style *style, int x,int y)
+{
+ const bool filled = false;
+
+ view->drawRectangle(style->color, core::style::Color::SHADING_NORMAL,filled,
+ x + this->x, y + this->y, this->width, this->height);
+}
+
/**
* Return whether this rectangle and otherRect intersect. If yes,
* return the intersection rectangle in dest.
- *
- * \todo The function has been copied from gdktrectangle.c. Is this relevant
- * for copyright?
*/
bool Rectangle::intersectsWith (Rectangle *otherRect, Rectangle *dest)
{
@@ -121,6 +135,18 @@ Circle::Circle (int x, int y, int radius)
this->radius = radius;
}
+/*
+ * Draw circle in view relative to point (x,y).
+ */
+void Circle::draw (core::View *view, core::style::Style *style, int x, int y)
+{
+ const bool filled = false;
+
+ view->drawArc(style->color, core::style::Color::SHADING_NORMAL, filled,
+ x + this->x, y + this->y, 2 * this->radius, 2 * this->radius,
+ 0, 360);
+}
+
bool Circle::isPointWithin (int x, int y)
{
return
@@ -142,6 +168,27 @@ Polygon::~Polygon ()
delete points;
}
+/*
+ * Draw polygon in view relative to point (x,y).
+ */
+void Polygon::draw (core::View *view, core::style::Style *style, int x, int y)
+{
+ if (points->size()) {
+ int i;
+ const bool filled = false;
+ int (*pointArray)[2] =
+ (int (*)[2]) malloc(points->size() * sizeof(*pointArray));
+
+ for (i = 0; i < points->size(); i++) {
+ pointArray[i][0] = x + points->getRef(i)->x;
+ pointArray[i][1] = y + points->getRef(i)->y;
+ }
+ view->drawPolygon(style->color, core::style::Color::SHADING_NORMAL,
+ filled, pointArray, i);
+ free(pointArray);
+ }
+}
+
void Polygon::addPoint (int x, int y)
{
points->increase ();
@@ -180,8 +227,8 @@ bool Polygon::linesCross(int ax1, int ay1, int ax2, int ay2,
bool cross =
linesCross0 (ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) &&
linesCross0 (bx1, by1, bx2, by2, ax1, ay1, ax2, ay2);
- //printf ("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n",
- // ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No");
+ _MSG("(%d, %d) - (%d, %d) and (%d, %d) - (%d, %d) cross? %s.\n",
+ ax1, ay1, ax2, ay2, bx1, by1, bx2, by2, cross ? "Yes" : "No");
return cross;
}
@@ -248,7 +295,7 @@ void Region::addRectangle (Rectangle *rPointer)
r->y = misc::min(r->y, ownRect->y);
r->width = combinedWidth;
r->height = combinedHeight;
-
+
rectangleList->removeRef (ownRect);
}
}
diff --git a/dw/types.hh b/dw/types.hh
index cd35e1f6..420a500f 100644
--- a/dw/types.hh
+++ b/dw/types.hh
@@ -8,7 +8,9 @@
namespace dw {
namespace core {
-using namespace lout;
+namespace style {
+ class Style;
+}
enum HPosition
{
@@ -30,6 +32,8 @@ enum VPosition
VPOS_NO_CHANGE
};
+enum ScrollCommand {SCREEN_UP_CMD, SCREEN_DOWN_CMD, LINE_UP_CMD, LINE_DOWN_CMD,
+ LEFT_CMD, RIGHT_CMD, TOP_CMD, BOTTOM_CMD};
/*
* Different "layers" may be highlighted in a widget.
@@ -50,10 +54,12 @@ struct Point
/**
* \brief Abstract interface for different shapes.
*/
-class Shape: public object::Object
+class Shape: public lout::object::Object
{
public:
virtual bool isPointWithin (int x, int y) = 0;
+ virtual void draw (core::View *view, core::style::Style *style, int x,
+ int y) = 0;
};
/**
@@ -70,6 +76,7 @@ public:
inline Rectangle () { }
Rectangle (int x, int y, int width, int height);
+ void draw (core::View *view, core::style::Style *style, int x, int y);
bool intersectsWith (Rectangle *otherRect, Rectangle *dest);
bool isSubsetOf (Rectangle *otherRect);
bool isPointWithin (int x, int y);
@@ -86,6 +93,7 @@ public:
Circle (int x, int y, int radius);
+ void draw (core::View *view, core::style::Style *style, int x, int y);
bool isPointWithin (int x, int y);
};
@@ -95,12 +103,12 @@ public:
class Polygon: public Shape
{
private:
- misc::SimpleVector<Point> *points;
+ lout::misc::SimpleVector<Point> *points;
int minx, miny, maxx, maxy;
/**
* \brief Return the z-coordinate of the vector product of two
- * vectors, whose z-coordinate is 0 (so that x and y of
+ * vectors, whose z-coordinate is 0 (so that x and y of
* the vector product is 0, too).
*/
inline int zOfVectorProduct(int x1, int y1, int x2, int y2) {
@@ -116,6 +124,7 @@ public:
Polygon ();
~Polygon ();
+ void draw (core::View *view, core::style::Style *style, int x, int y);
void addPoint (int x, int y);
bool isPointWithin (int x, int y);
};
@@ -130,8 +139,8 @@ public:
class Region
{
private:
- container::typed::List <Rectangle> *rectangleList;
-
+ lout::container::typed::List <Rectangle> *rectangleList;
+
public:
Region ();
~Region ();
@@ -140,7 +149,7 @@ public:
void addRectangle (Rectangle *r);
- container::typed::Iterator <Rectangle> rectangles ()
+ lout::container::typed::Iterator <Rectangle> rectangles ()
{
return rectangleList->iterator ();
};
@@ -179,22 +188,20 @@ struct Content
END = 1 << 1,
TEXT = 1 << 2,
WIDGET = 1 << 3,
- ANCHOR = 1 << 4,
- BREAK = 1 << 5,
+ BREAK = 1 << 4,
FLOAT_REF = 1 << 6, /** \todo A bit ugly. */
ALL = 0xff,
REAL_CONTENT = 0xff ^ (START | END | FLOAT_REF),
SELECTION_CONTENT = TEXT | WIDGET | BREAK
};
/* Content is embedded in struct Word therefore we
- * try to be space efficient.
+ * try to be space efficient.
*/
short type;
bool space;
union {
const char *text;
Widget *widget;
- char *anchor;
int breakSpace;
};
};
diff --git a/dw/ui.cc b/dw/ui.cc
index f857e387..058dfde8 100644
--- a/dw/ui.cc
+++ b/dw/ui.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -28,7 +27,8 @@ namespace dw {
namespace core {
namespace ui {
-using namespace object;
+using namespace lout;
+using namespace lout::object;
int Embed::CLASS_ID = -1;
@@ -62,11 +62,26 @@ void Embed::sizeAllocateImpl (Allocation *allocation)
void Embed::enterNotifyImpl (core::EventCrossing *event)
{
resource->emitEnter();
+ Widget::enterNotifyImpl(event);
}
void Embed::leaveNotifyImpl (core::EventCrossing *event)
{
resource->emitLeave();
+ Widget::leaveNotifyImpl(event);
+}
+
+bool Embed::buttonPressImpl (core::EventButton *event)
+{
+ bool handled;
+
+ if (event->button == 3) {
+ resource->emitClicked(event);
+ handled = true;
+ } else {
+ handled = false;
+ }
+ return handled;
}
void Embed::setWidth (int width)
@@ -84,6 +99,16 @@ void Embed::setDescent (int descent)
resource->setDescent (descent);
}
+void Embed::setDisplayed (bool displayed)
+{
+ resource->setDisplayed (displayed);
+}
+
+void Embed::setEnabled (bool enabled)
+{
+ resource->setEnabled (enabled);
+}
+
void Embed::draw (View *view, Rectangle *area)
{
drawWidgetBox (view, area, false);
@@ -183,6 +208,10 @@ void Resource::setDescent (int descent)
{
}
+void Resource::setDisplayed (bool displayed)
+{
+}
+
void Resource::draw (View *view, Rectangle *area)
{
}
@@ -201,31 +230,23 @@ void Resource::emitLeave ()
activateEmitter.emitLeave(this);
}
-// ----------------------------------------------------------------------
-
-bool ButtonResource::ClickedEmitter::emitToReceiver (lout::signal::Receiver
- *receiver,
- int signalNo,
- int argc,
- Object **argv)
+bool Resource::ClickedEmitter::emitToReceiver(lout::signal::Receiver *receiver,
+ int signalNo, int argc,
+ Object **argv)
{
((ClickedReceiver*)receiver)
- ->clicked ((ButtonResource*)((Pointer*)argv[0])->getValue (),
- ((Integer*)argv[1])->getValue (),
- ((Integer*)argv[2])->getValue (),
- ((Integer*)argv[3])->getValue ());
+ ->clicked ((Resource*)((Pointer*)argv[0])->getValue (),
+ (EventButton*)((Pointer*)argv[1])->getValue());
return false;
}
-void ButtonResource::ClickedEmitter::emitClicked (ButtonResource *resource,
- int buttonNo, int x, int y)
+void Resource::ClickedEmitter::emitClicked (Resource *resource,
+ EventButton *event)
{
- Integer i1 (buttonNo);
- Integer i2 (x);
- Integer i3 (y);
- Pointer p (resource);
- Object *argv[4] = { &p, &i1, &i2, &i3 };
- emitVoid (0, 4, argv);
+ Pointer p1 (resource);
+ Pointer p2 (event);
+ Object *argv[2] = { &p1, &p2 };
+ emitVoid (0, 2, argv);
}
// ----------------------------------------------------------------------
diff --git a/dw/ui.hh b/dw/ui.hh
index de3e1b2b..02aab093 100644
--- a/dw/ui.hh
+++ b/dw/ui.hh
@@ -14,24 +14,24 @@ namespace core {
* UI resources are another abstraction for Dw widgets, which are not
* fully implemented in a platform-independent way. Typically, they
* involve creating widgets, which the underlying UI toolkit provides.
- *
+ *
* As you see in this diagram:
- *
+ *
* \dot
* digraph G {
* node [shape=record, fontname=Helvetica, fontsize=10];
* edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
* labelfontsize=10, color="#404040", labelfontcolor="#000080"];
* fontname=Helvetica; fontsize=10;
- *
+ *
* subgraph cluster_core {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core";
- *
+ *
* subgraph cluster_ui {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::core::ui";
- *
+ *
* Embed [URL="\ref dw::core::ui::Embed"];
* Resource [color="#a0a0a0", URL="\ref dw::core::ui::Resource"];
* LabelButtonResource [color="#a0a0a0",
@@ -40,19 +40,19 @@ namespace core {
* URL="\ref dw::core::ui::EntryResource"];
* etc [color="#a0a0a0", label="..."];
* }
- *
+ *
* Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
* }
- *
+ *
* subgraph cluster_fltk {
* style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
* label="dw::fltk::ui";
- *
+ *
* FltkLabelButtonResource
* [URL="\ref dw::fltk::ui::FltkLabelButtonResource"];
* FltkEntryResource [URL="\ref dw::fltk::ui::FltkEntryResource"];
* }
- *
+ *
* Widget -> Embed;
* Embed -> Resource [arrowhead="open", arrowtail="none",
* headlabel="1", taillabel="1"];
@@ -63,16 +63,16 @@ namespace core {
* EntryResource -> FltkEntryResource;
* }
* \enddot
- *
+ *
* <center>[\ref uml-legend "legend"]</center>
- *
+ *
* there are several levels:
- *
+ *
* <ol>
* <li> The Dw widget is dw::core::ui::Embed. It delegates most to
* dw::core::ui::Resource, which has similar methods like
* dw::core::Widget.
- *
+ *
* <li> There are several sub interfaces of dw::core::ui::Resource, which
* may provide methods, as e.g. dw::core::ui::ListResource::addItem. In a
* platform independent context, you can cast the result of
@@ -80,54 +80,54 @@ namespace core {
* know, which one is used. E.g., if you know, that a given instance
* dw::core::ui::Embed refers to a dw::core::ui::ListResource, you can
* write something like:
- *
+ *
* \code
* dw::core::ui::Embed *embed;
* //...
* ((dw::core::ui::ListResource*)embed->getResource ())->addItem ("Hello!");
* \endcode
- *
+ *
* <li> These sub classes are then fully implemented in a platform specific
* way. For an example, look at dw::fltk::ui.
* </ol>
- *
+ *
* There is a factory interface, dw::core::ui::ResourceFactory, which
* provides methods for creating common resources. By calling
* dw::core::Layout::getResourceFactory, which calls
* dw::core::Platform::getResourceFactory, you get the factory for the used
* platform.
- *
+ *
* It is possible to define additional sub classes of
* dw::core::ui::Resource, but since they are not provided by
* dw::core::ui::ResourceFactory, you have to define some other
* abstractions, if you want to remain platform independent.
- *
- *
+ *
+ *
* <h3>...</h3>
- *
- *
+ *
+ *
* <h3>Resouces needed for HTML</h3>
- *
+ *
* This chapter describes, how the form controls defined by HTML are
* implemented in Dw. Some of them do not refer to UI resources, but to
* other widgets, links to the respective documentations are provided
* here.
- *
+ *
* <h4>Resouces created with \<INPUT\></h4>
- *
+ *
* The HTML \<INPUT\> is always implemented by using UI
* resources. \<INPUT\> element has the following attributes:
- *
+ *
* <table>
* <tr><th>Attribute <th>Implementation
- * <tr><td>type <td>This defines the resource you have to instanciate.
+ * <tr><td>type <td>This defines the resource you have to instantiate.
* <tr><td>name <td>Not needed within Dw.
* <tr><td>value <td>The initial value is treated differently by different
* resources.
* <tr><td>checked <td>Parameter to
* dw::core::ui::ResourceFactory::createCheckButtonResource
* and dw::core::ui::ResourceFactory::createRadioButtonResource.
- * <tr><td>disabled <td>This is provided for all resources by
+ * <tr><td>disabled <td>This is provided for all resources by
* dw::core::ui::Resource::setEnabled.
* <tr><td>readonly <td>This is provided by
* dw::core::ui::TextResource::setEditable.
@@ -146,10 +146,10 @@ namespace core {
* <tr><td>onchange <td>Not supported currently.
* <tr><td>accept <td>Not supported currently.
* </table>
- *
+ *
* For the different values of \em type, the following resources can be
* used:
- *
+ *
* <table>
* <tr><th>Type <th>Resource
* <th>Factory Method
@@ -175,35 +175,35 @@ namespace core {
* <tr><td>file <td>Not supported currently.
* <td>-
* </table>
- *
+ *
* <h4>\<SELECT\>, \<OPTGROUP\>, and \<OPTION\></h4>
- *
+ *
* \<SELECT\> is implemented either by dw::core::ui::OptionMenuResource
* (better suitable for \em size = 1 and single selection) or
* dw::core::ui::ListResource, which have a common base,
* dw::core::ui::SelectionResource. In the latter case, \em size must be
* specified via dw::core::style::Style.
- *
+ *
* Factory methods are dw::core::ui::ResourceFactory::createListResource and
* dw::core::ui::ResourceFactory::createOptionMenuResource.
- *
+ *
* \<OPTION\>'s are added via dw::core::ui::SelectionResource::addItem.
- *
+ *
* \<OPTGROUP\> are created by using dw::core::ui::SelectionResource::pushGroup
* and dw::core::ui::SelectionResource::popGroup.
- *
+ *
* For lists, the selection mode must be set in
* dw::core::ui::ResourceFactory::createListResource.
- *
+ *
* <h4>\<TEXTAREA\></h4>
- *
+ *
* \<TEXTAREA\> is implemented by dw::core::ui::MultiLineTextResource,
* the factory method is
* dw::core::ui::ResourceFactory::createMultiLineTextResource.
* dw::core::ui::TextResource::setEditable can be used, as for entries.
- *
+ *
* <h4>\<BUTTON\></h4>
- *
+ *
* For handling \<BUTTON\>, dw::core::ui::ComplexButtonResource should be used,
* with a dw::Textblock inside, and relief = true. The contents of \<BUTTON\>
* is then added to the dw::Textblock.
@@ -232,6 +232,7 @@ protected:
void sizeAllocateImpl (Allocation *allocation);
void enterNotifyImpl (core::EventCrossing *event);
void leaveNotifyImpl (core::EventCrossing *event);
+ bool buttonPressImpl (core::EventButton *event);
public:
static int CLASS_ID;
@@ -242,6 +243,8 @@ public:
void setWidth (int width);
void setAscent (int ascent);
void setDescent (int descent);
+ void setDisplayed (bool displayed);
+ void setEnabled (bool enabled);
void draw (View *view, Rectangle *area);
Iterator *iterator (Content::Type mask, bool atEnd);
void setStyle (style::Style *style);
@@ -271,6 +274,14 @@ public:
virtual void enter (Resource *resource) = 0;
virtual void leave (Resource *resource) = 0;
};
+ /**
+ * \brief Receiver interface for the "clicked" signal.
+ */
+ class ClickedReceiver: public lout::signal::Receiver
+ {
+ public:
+ virtual void clicked (Resource *resource, EventButton *event) = 0;
+ };
private:
class ActivateEmitter: public lout::signal::Emitter
@@ -286,8 +297,20 @@ private:
void emitLeave (Resource *resource);
};
+ class ClickedEmitter: public lout::signal::Emitter
+ {
+ protected:
+ bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
+ int argc, Object **argv);
+ public:
+ inline void connectClicked (ClickedReceiver *receiver) {
+ connect (receiver); }
+ void emitClicked (Resource *resource, EventButton *event);
+ };
+
Embed *embed;
ActivateEmitter activateEmitter;
+ ClickedEmitter clickedEmitter;
void emitEnter ();
void emitLeave ();
@@ -301,10 +324,12 @@ protected:
inline void emitActivate () {
return activateEmitter.emitActivate (this); }
+ inline void emitClicked (EventButton *event) {
+ clickedEmitter.emitClicked (this, event); }
public:
inline Resource () { embed = NULL; }
-
+
virtual ~Resource ();
virtual void sizeRequest (Requisition *requisition) = 0;
@@ -313,8 +338,9 @@ public:
virtual void setWidth (int width);
virtual void setAscent (int ascent);
virtual void setDescent (int descent);
+ virtual void setDisplayed (bool displayed);
virtual void draw (View *view, Rectangle *area);
- virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
+ virtual Iterator *iterator (Content::Type mask, bool atEnd) = 0;
virtual void setStyle (style::Style *style);
virtual bool isEnabled () = 0;
@@ -322,44 +348,13 @@ public:
inline void connectActivate (ActivateReceiver *receiver) {
activateEmitter.connectActivate (receiver); }
+ inline void connectClicked (ClickedReceiver *receiver) {
+ clickedEmitter.connectClicked (receiver); }
};
class ButtonResource: public Resource
-{
-public:
- /**
- * \brief Receiver interface for the "clicked" signal.
- */
- class ClickedReceiver: public lout::signal::Receiver
- {
- public:
- virtual void clicked (ButtonResource *resource, int buttonNo, int x,
- int y) = 0;
- };
-
-private:
- class ClickedEmitter: public lout::signal::Emitter
- {
- protected:
- bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
- int argc, Object **argv);
- public:
- inline void connectClicked (ClickedReceiver *receiver) {
- connect (receiver); }
- void emitClicked (ButtonResource *resource, int buttonNo, int x, int y);
- };
-
- ClickedEmitter clickedEmitter;
-
-protected:
- inline void emitClicked (int buttonNo, int x, int y) {
- return clickedEmitter.emitClicked (this, buttonNo, x, y); }
-
-public:
- inline void connectClicked (ClickedReceiver *receiver) {
- clickedEmitter.connectClicked (receiver); }
-};
+{};
/**
* \brief Interface for labelled buttons resources.
@@ -367,7 +362,7 @@ public:
class LabelButtonResource: public ButtonResource
{
public:
- Iterator *iterator (Content::Type mask, bool atEnd);
+ Iterator *iterator (Content::Type mask, bool atEnd);
virtual const char *getLabel () = 0;
virtual void setLabel (const char *label) = 0;
@@ -397,7 +392,7 @@ protected:
virtual Platform *createPlatform () = 0;
virtual void setLayout (Layout *layout) = 0;
-
+
virtual int reliefXThickness () = 0;
virtual int reliefYThickness () = 0;
@@ -419,14 +414,14 @@ public:
};
/**
- * \brief Base interface for dw::core::ui::ListResource and
+ * \brief Base interface for dw::core::ui::ListResource and
* dw::core::ui::OptionMenuResource.
*/
class SelectionResource: public Resource
{
public:
virtual void addItem (const char *str, bool enabled, bool selected) = 0;
-
+
virtual void pushGroup (const char *name, bool enabled) = 0;
virtual void popGroup () = 0;
@@ -448,7 +443,7 @@ public:
/**
* \brief Exactly one item is selected, except possibly at the beginning.
- *
+ *
* If no item is selected initially, no one is selected automatically.
* The user may not unselect the only selected item.
*/
@@ -476,7 +471,7 @@ class OptionMenuResource: public SelectionResource
class TextResource: public Resource
{
public:
- Iterator *iterator (Content::Type mask, bool atEnd);
+ Iterator *iterator (Content::Type mask, bool atEnd);
virtual const char *getText () = 0;
virtual void setText (const char *text) = 0;
@@ -505,7 +500,7 @@ public:
class CheckButtonResource: public ToggleButtonResource
{
public:
- Iterator *iterator (Content::Type mask, bool atEnd);
+ Iterator *iterator (Content::Type mask, bool atEnd);
};
class RadioButtonResource: public ToggleButtonResource
@@ -529,14 +524,14 @@ public:
*/
virtual GroupIterator *groupIterator () = 0;
- Iterator *iterator (Content::Type mask, bool atEnd);
+ Iterator *iterator (Content::Type mask, bool atEnd);
};
/**
* \brief A factory for the common resource.
*/
-class ResourceFactory: public object::Object
+class ResourceFactory: public lout::object::Object
{
public:
virtual LabelButtonResource *createLabelButtonResource (const char *label)
@@ -545,10 +540,10 @@ public:
bool relief)
= 0;
virtual ListResource *createListResource (ListResource::SelectionMode
- selectionMode) = 0;
+ selectionMode, int rows) = 0;
virtual OptionMenuResource *createOptionMenuResource () = 0;
- virtual EntryResource *createEntryResource (int maxLength,
- bool password) = 0;
+ virtual EntryResource *createEntryResource (int maxLength, bool password,
+ const char *label) = 0;
virtual MultiLineTextResource *createMultiLineTextResource (int cols,
int rows) = 0;
virtual CheckButtonResource *createCheckButtonResource (bool activated) = 0;
diff --git a/dw/view.hh b/dw/view.hh
index ef604549..d0f6c6b9 100644
--- a/dw/view.hh
+++ b/dw/view.hh
@@ -13,7 +13,7 @@ namespace core {
*
* \sa\ref dw-overview, \ref dw-layout-views
*/
-class View: public object::Object
+class View: public lout::object::Object
{
public:
/*
@@ -34,7 +34,7 @@ public:
virtual void setCanvasSize (int width, int ascent, int descent) = 0;
/**
- * \brief Set the cursor appearance.
+ * \brief Set the cursor appearance.
*/
virtual void setCursor (style::Cursor cursor) = 0;
@@ -42,7 +42,7 @@ public:
* \brief Set the background of the view.
*/
virtual void setBgColor (style::Color *color) = 0;
-
+
/*
* ---------------------------------------------------------
* Scrolling and Related. Only usesViewport must be
@@ -80,6 +80,11 @@ public:
virtual void scrollTo (int x, int y) = 0;
/**
+ * \brief Scroll the viewport as commanded.
+ */
+ virtual void scroll (ScrollCommand) { };
+
+ /**
* \brief Set the viewport size.
*
* Does not have to be implemented, when usesViewport returns false.
@@ -107,7 +112,7 @@ public:
/**
* \brief Called before drawing.
*
- * All actual drawing operations will be enclosed into calls of
+ * All actual drawing operations will be enclosed into calls of
* dw::core:View::startDrawing and dw::core:View::finishDrawing. They
* may be implemented, e.g. when a backing
* pixmap is used, to prevent flickering. StartDrawing() will then
@@ -166,7 +171,7 @@ public:
int x, int y, int width, int height) = 0;
virtual void drawArc (style::Color *color,
style::Color::Shading shading, bool filled,
- int x, int y, int width, int height,
+ int centerX, int centerY, int width, int height,
int angle1, int angle2) = 0;
virtual void drawPolygon (style::Color *color,
style::Color::Shading shading,
diff --git a/dw/widget.cc b/dw/widget.cc
index b664b433..751cdcdf 100644
--- a/dw/widget.cc
+++ b/dw/widget.cc
@@ -14,222 +14,22 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "core.hh"
+#include "../lout/msg.h"
#include "../lout/debug.hh"
+using namespace lout;
using namespace lout::object;
namespace dw {
namespace core {
-bool Widget::EventReceiver::buttonPress (Widget *widget, EventButton *event)
-{
- return false;
-}
-
-bool Widget::EventReceiver::buttonRelease (Widget *widget, EventButton *event)
-{
- return false;
-}
-
-bool Widget::EventReceiver::motionNotify (Widget *widget, EventMotion *event)
-{
- return false;
-}
-
-void Widget::EventReceiver::enterNotify (Widget *widget, EventCrossing *event)
-{
-}
-
-void Widget::EventReceiver::leaveNotify (Widget *widget, EventCrossing *event)
-{
-}
-
-
-bool Widget::EventEmitter::emitToReceiver (lout::signal::Receiver *receiver,
- int signalNo,
- int argc, Object **argv)
-{
- EventReceiver *eventReceiver = (EventReceiver*)receiver;
-
- switch (signalNo) {
- case BUTTON_PRESS:
- return eventReceiver->buttonPress ((Widget*)argv[0],
- (EventButton*)argv[1]);
-
- case BUTTON_RELEASE:
- return eventReceiver->buttonRelease ((Widget*)argv[0],
- (EventButton*)argv[1]);
-
- case MOTION_NOTIFY:
- return eventReceiver->motionNotify ((Widget*)argv[0],
- (EventMotion*)argv[1]);
-
- case ENTER_NOTIFY:
- eventReceiver->enterNotify ((Widget*)argv[0],
- (EventCrossing*)argv[1]);
- break;
-
- case LEAVE_NOTIFY:
- eventReceiver->leaveNotify ((Widget*)argv[1],
- (EventCrossing*)argv[0]);
- break;
-
- default:
- misc::assertNotReached ();
- }
-
- /* Compiler happiness. */
- return false;
-}
-
-bool Widget::EventEmitter::emitButtonPress (Widget *widget, EventButton *event)
-{
- Object *argv[2] = { widget, event };
- return emitBool (BUTTON_PRESS, 2, argv);
-}
-
-bool Widget::EventEmitter::emitButtonRelease (Widget *widget,
- EventButton *event)
-{
- Object *argv[2] = { widget, event };
- return emitBool (BUTTON_RELEASE, 2, argv);
-}
-
-bool Widget::EventEmitter::emitMotionNotify (Widget *widget,
- EventMotion *event)
-{
- Object *argv[2] = { widget, event };
- return emitBool (MOTION_NOTIFY, 2, argv);
-}
-
-void Widget::EventEmitter::emitEnterNotify (Widget *widget,
- EventCrossing *event)
-{
- Object *argv[2] = { widget, event };
- emitVoid (ENTER_NOTIFY, 2, argv);
-}
-
-void Widget::EventEmitter::emitLeaveNotify (Widget *widget,
- EventCrossing *event)
-{
- Object *argv[2] = { widget, event };
- emitVoid (LEAVE_NOTIFY, 2, argv);
-}
-
-// ----------------------------------------------------------------------
-
-bool Widget::LinkReceiver::enter (Widget *widget, int link, int img,
- int x, int y)
-{
- return false;
-}
-
-bool Widget::LinkReceiver::press (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- return false;
-}
-
-bool Widget::LinkReceiver::release (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- return false;
-}
-
-bool Widget::LinkReceiver::click (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- return false;
-}
-
-
-bool Widget::LinkEmitter::emitToReceiver (lout::signal::Receiver *receiver,
- int signalNo,
- int argc, Object **argv)
-{
- LinkReceiver *linkReceiver = (LinkReceiver*)receiver;
-
- switch (signalNo) {
- case ENTER:
- return linkReceiver->enter ((Widget*)argv[0],
- ((Integer*)argv[1])->getValue (),
- ((Integer*)argv[2])->getValue (),
- ((Integer*)argv[3])->getValue (),
- ((Integer*)argv[4])->getValue ());
-
- case PRESS:
- return linkReceiver->press ((Widget*)argv[0],
- ((Integer*)argv[1])->getValue (),
- ((Integer*)argv[2])->getValue (),
- ((Integer*)argv[3])->getValue (),
- ((Integer*)argv[4])->getValue (),
- (EventButton*)argv[5]);
-
- case RELEASE:
- return linkReceiver->release ((Widget*)argv[0],
- ((Integer*)argv[1])->getValue (),
- ((Integer*)argv[2])->getValue (),
- ((Integer*)argv[3])->getValue (),
- ((Integer*)argv[4])->getValue (),
- (EventButton*)argv[5]);
-
- case CLICK:
- return linkReceiver->click ((Widget*)argv[0],
- ((Integer*)argv[1])->getValue (),
- ((Integer*)argv[2])->getValue (),
- ((Integer*)argv[3])->getValue (),
- ((Integer*)argv[4])->getValue (),
- (EventButton*)argv[5]);
-
- default:
- misc::assertNotReached ();
- }
-
- /* Compiler happiness. */
- return false;
-}
-
-bool Widget::LinkEmitter::emitEnter (Widget *widget, int link, int img,
- int x, int y)
-{
- Integer ilink (link), iimg (img), ix (x), iy (y);
- Object *argv[5] = { widget, &ilink, &iimg, &ix, &iy };
- return emitBool (ENTER, 5, argv);
-}
-
-bool Widget::LinkEmitter::emitPress (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- Integer ilink (link), iimg (img), ix (x), iy (y);
- Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
- return emitBool (PRESS, 6, argv);
-}
-
-bool Widget::LinkEmitter::emitRelease (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- Integer ilink (link), iimg (img), ix (x), iy (y);
- Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
- return emitBool (RELEASE, 6, argv);
-}
-
-bool Widget::LinkEmitter::emitClick (Widget *widget, int link, int img,
- int x, int y, EventButton *event)
-{
- Integer ilink (link), iimg (img), ix (x), iy (y);
- Object *argv[6] = { widget, &ilink, &iimg, &ix, &iy, event };
- return emitBool (CLICK, 6, argv);
-}
-
-
// ----------------------------------------------------------------------
int Widget::CLASS_ID = -1;
@@ -237,7 +37,7 @@ int Widget::CLASS_ID = -1;
Widget::Widget ()
{
registerName ("dw::core::Widget", &CLASS_ID);
-
+
flags = (Flags)(NEEDS_RESIZE | EXTREMES_CHANGED | HAS_CONTENTS);
parent = NULL;
layout = NULL;
@@ -317,7 +117,7 @@ void Widget::queueDrawArea (int x, int y, int width, int height)
{
/** \todo Maybe only the intersection? */
layout->queueDraw (x + allocation.x, y + allocation.y, width, height);
- //printf("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
+ _MSG("Widget::queueDrawArea x=%d y=%d w=%d h=%d\n", x, y, width, height);
}
/**
@@ -460,35 +260,27 @@ void Widget::sizeAllocate (Allocation *allocation)
bool Widget::buttonPress (EventButton *event)
{
- bool b1 = buttonPressImpl (event);
- bool b2 = eventEmitter.emitButtonPress (this, event);
- return b1 || b2;
+ return buttonPressImpl (event);
}
bool Widget::buttonRelease (EventButton *event)
{
- bool b1 = buttonReleaseImpl (event);
- bool b2 = eventEmitter.emitButtonRelease (this, event);
- return b1 || b2;
+ return buttonReleaseImpl (event);
}
bool Widget::motionNotify (EventMotion *event)
{
- bool b1 = motionNotifyImpl (event);
- bool b2 = eventEmitter.emitMotionNotify (this, event);
- return b1 || b2;
+ return motionNotifyImpl (event);
}
void Widget::enterNotify (EventCrossing *event)
{
enterNotifyImpl (event);
- eventEmitter.emitEnterNotify (this, event);
}
void Widget::leaveNotify (EventCrossing *event)
{
leaveNotifyImpl (event);
- eventEmitter.emitLeaveNotify (this, event);
}
/**
@@ -497,7 +289,7 @@ void Widget::leaveNotify (EventCrossing *event)
* The old style is automatically unreferred, the new is referred. If this
* call causes the widget to change its size, dw::core::Widget::queueResize
* is called.
- */
+ */
void Widget::setStyle (style::Style *style)
{
bool sizeChanged;
@@ -549,7 +341,7 @@ style::Color *Widget::getBgColor ()
widget = widget->parent;
}
- fprintf (stderr, "No background color found!\n");
+ MSG_WARN("No background color found!\n");
return NULL;
}
@@ -694,10 +486,10 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget)
/* Search upwards. */
while (widget1 != widget2) {
if (widget1->parent == NULL) {
- fprintf (stderr, "widgets in different trees\n");
+ MSG_WARN("widgets in different trees\n");
return NULL;
}
-
+
widget1 = widget1->parent;
widget2 = widget2->parent;
}
@@ -708,7 +500,7 @@ Widget *Widget::getNearestCommonAncestor (Widget *otherWidget)
/**
* \brief Search recursively through widget.
- *
+ *
* Used by dw::core::Layout:getWidgetAtPoint.
*/
Widget *Widget::getWidgetAtPoint (int x, int y, int level)
@@ -740,7 +532,7 @@ Widget *Widget::getWidgetAtPoint (int x, int y, int level)
level + 1);
it->unref ();
-
+
if (childAtPoint)
return childAtPoint;
else
@@ -823,12 +615,20 @@ bool Widget::motionNotifyImpl (EventMotion *event)
return false;
}
-void Widget::enterNotifyImpl (EventCrossing *event)
+void Widget::enterNotifyImpl (EventCrossing *)
{
+ core::style::Tooltip *tooltip = getStyle()->x_tooltip;
+
+ if (tooltip)
+ tooltip->onEnter();
}
-void Widget::leaveNotifyImpl (EventCrossing *event)
+void Widget::leaveNotifyImpl (EventCrossing *)
{
+ core::style::Tooltip *tooltip = getStyle()->x_tooltip;
+
+ if (tooltip)
+ tooltip->onLeave();
}
void Widget::removeChild (Widget *child)
diff --git a/dw/widget.hh b/dw/widget.hh
index fde48ecc..013be27b 100644
--- a/dw/widget.hh
+++ b/dw/widget.hh
@@ -20,149 +20,16 @@ namespace core {
*
* \sa\ref dw-overview, \ref dw-layout-widgets
*/
-class Widget: public identity::IdentifiableObject
+class Widget: public lout::identity::IdentifiableObject
{
friend class Layout;
-public:
- class EventReceiver: public lout::signal::Receiver
- {
- public:
- virtual bool buttonPress (Widget *widget, EventButton *event);
- virtual bool buttonRelease (Widget *widget, EventButton *event);
- virtual bool motionNotify (Widget *widget, EventMotion *event);
- virtual void enterNotify (Widget *widget, EventCrossing *event);
- virtual void leaveNotify (Widget *widget, EventCrossing *event);
- };
-
- /**
- * \brief This receiver is for signals related to HTML pages.
- *
- * The \em link argument to all signals defines a number, which has
- * been passed before, e.g. by setting dw::core::style::Style::x_link.
- * When defining this number (e.g in dw::core::style::Style::x_link),
- * and when receiving the signal, the caller must interpret these numbers
- * in a consistent way. In the HTML link block, this number is an index
- * to an array of URLs.
- *
- * \em link = -1 represents an undefined link.
- *
- * The \em img argument to all signals defines a number which has
- * been passed before, e.g. by setting dw::core::style::Style::x_img.
- * When defining this number (e.g in dw::core::style::Style::x_img),
- * and when receiving the signal, the caller must interpret these numbers
- * in a consistent way. In the HTML link block, this number is an index
- * to an array of structures containing image information.
- *
- * \em img = -1 represents an undefined image.
- *
- * \em x and \em y define the coordinates within the link area. They are
- * only used for server-side image maps, see dw::Image.
- *
- * \sa dw::Image, dw::Textblock
- */
- class LinkReceiver: public lout::signal::Receiver
- {
- public:
- /**
- * \brief Called, when a link is entered, left, or the position has
- * changed.
- *
- * When a link is entered, this method is called with the respective
- * arguments. When a link is left, this method is called with all
- * three arguments (\em link, \em x, \em y) set to -1.
- *
- * When coordinates are supported, a change of the coordinates also
- * causes emitting this signal.
- */
- virtual bool enter (Widget *widget, int link, int img, int x, int y);
-
- /**
- * \brief Called, when the user has pressed the mouse button on a
- * link (but not yet released).
- *
- * The causing event is passed as \em event.
- */
- virtual bool press (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
-
- /**
- * \brief Called, when the user has released the mouse button on a
- * link.
- *
- * The causing event is passed as \em event.
- */
- virtual bool release (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
-
- /**
- * \brief Called, when the user has clicked on a link.
- *
- * For mouse interaction, this is equivalent to "press" and "release"
- * on the same link. In this case, \em event contains the "release"
- * event.
- *
- * When activating links via keyboard is supported, only a "clicked"
- * signal will be emitted, and \em event will be NULL.
- */
- virtual bool click (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
- };
-
-private:
- class EventEmitter: public lout::signal::Emitter
- {
- private:
- enum { BUTTON_PRESS, BUTTON_RELEASE, MOTION_NOTIFY, ENTER_NOTIFY,
- LEAVE_NOTIFY };
-
- protected:
- bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
- int argc, Object **argv);
-
- public:
- inline void connectEvent (EventReceiver *receiver)
- { connect (receiver); }
-
- bool emitButtonPress (Widget *widget, EventButton *event);
- bool emitButtonRelease (Widget *widget, EventButton *event);
- bool emitMotionNotify (Widget *widget, EventMotion *event);
- void emitEnterNotify (Widget *widget, EventCrossing *event);
- void emitLeaveNotify (Widget *widget, EventCrossing *event);
- };
-
- class LinkEmitter: public lout::signal::Emitter
- {
- private:
- enum { ENTER, PRESS, RELEASE, CLICK };
-
- protected:
- bool emitToReceiver (lout::signal::Receiver *receiver, int signalNo,
- int argc, Object **argv);
-
- public:
- inline void connectLink (LinkReceiver *receiver) { connect (receiver); }
-
- bool emitEnter (Widget *widget, int link, int img, int x, int y);
- bool emitPress (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
- bool emitRelease (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
- bool emitClick (Widget *widget, int link, int img, int x, int y,
- EventButton *event);
- };
-
- EventEmitter eventEmitter;
-
- style::Style *style;
-
-
protected:
enum Flags {
/**
* \brief Set, when dw::core::Widget::requisition is not up to date
* anymore.
- */
+ */
NEEDS_RESIZE = 1 << 0,
/**
@@ -175,7 +42,7 @@ protected:
/**
* \brief Set, when dw::core::Widget::extremes is not up to date
* anymore.
- */
+ */
EXTREMES_CHANGED = 1 << 2,
/**
@@ -183,7 +50,7 @@ protected:
* methods are implemented.
*
* Will hopefully be removed, after redesigning the size model.
- */
+ */
USES_HINTS = 1 << 3,
/**
@@ -191,7 +58,7 @@ protected:
* some contents, e.g. an image, as opposed to a horizontal ruler.
*
* Will hopefully be removed, after redesigning the size model.
- */
+ */
HAS_CONTENTS = 1 << 4,
/**
@@ -207,6 +74,7 @@ private:
* \brief The parent widget, NULL for top-level widgets.
*/
Widget *parent;
+ style::Style *style;
Flags flags;
@@ -248,7 +116,6 @@ public:
int parentRef;
protected:
- LinkEmitter linkEmitter;
/**
* \brief The current allocation: size and position, always relative to the
@@ -266,7 +133,7 @@ protected:
inline void setFlags (Flags f) { flags = (Flags)(flags | f); }
inline void unsetFlags (Flags f) { flags = (Flags)(flags & ~f); }
-
+
inline void queueDraw ()
{
@@ -346,7 +213,7 @@ protected:
EventMotion *event, bool withinContent)
{ return layout->selectionState.buttonMotion (it, charPos, linkNo, event,
withinContent); }
-
+
inline bool selectionHandleEvent (SelectionState::EventType eventType,
Iterator *it, int charPos, int linkNo,
MousePositionEvent *event,
@@ -372,28 +239,6 @@ public:
inline bool needsAllocate () { return flags & NEEDS_ALLOCATE; }
inline bool extremesChanged () { return flags & EXTREMES_CHANGED; }
inline bool wasAllocated () { return flags & WAS_ALLOCATED; }
-
- inline void connectEvent (EventReceiver *receiver)
- { eventEmitter.connectEvent (receiver); }
-
- inline void connectLink (LinkReceiver *receiver)
- { linkEmitter.connectLink (receiver); }
-
- inline bool emitLinkEnter (int link, int img, int x, int y)
- { return linkEmitter.emitEnter (this, link, img, x, y); }
-
- inline bool emitLinkPress (int link, int img,
- int x, int y, EventButton *event)
- { return linkEmitter.emitPress (this, link, img, x, y, event); }
-
- inline bool emitLinkRelease (int link, int img,
- int x, int y, EventButton *event)
- { return linkEmitter.emitRelease (this, link, img, x, y, event); }
-
- inline bool emitLinkClick (int link, int img,
- int x, int y, EventButton *event)
- { return linkEmitter.emitClick (this, link, img, x, y, event); }
-
inline bool usesHints () { return flags & USES_HINTS; }
inline bool hasContents () { return flags & HAS_CONTENTS; }
@@ -420,7 +265,7 @@ public:
bool motionNotify (EventMotion *event);
void enterNotify (EventCrossing *event);
void leaveNotify (EventCrossing *event);
-
+
virtual void setStyle (style::Style *style);
void setBgColor (style::Color *bgColor);
style::Color *getBgColor ();
diff --git a/install-dpi-local b/install-dpi-local
index e843e6c1..d83287f8 100755
--- a/install-dpi-local
+++ b/install-dpi-local
@@ -12,10 +12,14 @@ if [ -r $BASE/dpi_socket_dir ] ; then
fi
if [ ! -x dpid/dpid ] ; then
- echo "You MUST run this script after make."
+ echo "This script may only be run AFTER make."
exit 1
fi
+# Try to communicate with any currently-running dpid to tell it
+# to stop itself and the dpi programs.
+dpidc stop
+
if [ ! -d $BASE ] ; then
mkdir $BASE
fi
@@ -23,8 +27,8 @@ if [ ! -d $BASE2 ] ; then
mkdir $BASE2
fi
-cp dpid/dpid dpid/dpidc $BASE
-strip $BASE/dpid
+cp dpid/dpid dpid/dpidc dpid/dpidrc $BASE
+strip $BASE/dpid $BASE/dpidc
cd dpi
for F in *.dpi ; do
diff --git a/lout/Makefile.am b/lout/Makefile.am
index 18e00cf2..5a246708 100644
--- a/lout/Makefile.am
+++ b/lout/Makefile.am
@@ -11,4 +11,5 @@ liblout_a_SOURCES = \
object.cc \
object.hh \
signal.cc \
- signal.hh
+ signal.hh \
+ msg.h
diff --git a/lout/container.cc b/lout/container.cc
index 0b00c195..0a3d6acd 100644
--- a/lout/container.cc
+++ b/lout/container.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -43,41 +42,41 @@ Iterator::Iterator()
Iterator::Iterator(const Iterator &it2)
{
impl = it2.impl;
- if(impl)
+ if (impl)
impl->ref();
}
Iterator::Iterator(Iterator &it2)
{
impl = it2.impl;
- if(impl)
+ if (impl)
impl->ref();
}
Iterator &Iterator::operator=(const Iterator &it2)
{
- if(impl)
+ if (impl)
impl->unref();
impl = it2.impl;
- if(impl)
+ if (impl)
impl->ref();
return *this;
}
Iterator &Iterator::operator=(Iterator &it2)
{
- if(impl)
+ if (impl)
impl->unref();
impl = it2.impl;
- if(impl)
+ if (impl)
impl->ref();
return *this;
}
Iterator::~Iterator()
{
- if(impl)
- impl->unref();
+ if (impl)
+ impl->unref();
}
// ----------------
@@ -88,8 +87,8 @@ void Collection::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append("{ ");
bool first = true;
- for(Iterator it = iterator(); it.hasNext(); ) {
- if(!first)
+ for (Iterator it = iterator(); it.hasNext(); ) {
+ if (!first)
sb->append(", ");
it.getNext()->intoStringBuffer(sb);
first = false;
@@ -117,27 +116,27 @@ Vector::~Vector()
void Vector::put(Object *newElement, int newPos)
{
- if(newPos == -1)
+ if (newPos == -1)
newPos = numElements;
// Old entry is overwritten.
- if(newPos < numElements) {
- if(ownerOfObjects && array[newPos])
+ if (newPos < numElements) {
+ if (ownerOfObjects && array[newPos])
delete array[newPos];
}
// Allocated memory has to be increased.
- if(newPos >= numAlloc) {
+ if (newPos >= numAlloc) {
while (newPos >= numAlloc)
numAlloc *= 2;
array = (Object**)realloc(array, numAlloc * sizeof(Object*));
}
// Insert NULL's into possible gap before new position.
- for(int i = numElements; i < newPos; i++)
+ for (int i = numElements; i < newPos; i++)
array[i] = NULL;
- if(newPos >= numElements)
+ if (newPos >= numElements)
numElements = newPos + 1;
array[newPos] = newElement;
@@ -146,8 +145,8 @@ void Vector::put(Object *newElement, int newPos)
void Vector::clear()
{
if (ownerOfObjects) {
- for(int i = 0; i < numElements; i++)
- if(array[i])
+ for (int i = 0; i < numElements; i++)
+ if (array[i])
delete array[i];
}
@@ -156,18 +155,18 @@ void Vector::clear()
void Vector::insert(Object *newElement, int pos)
{
- if(pos >= numElements)
+ if (pos >= numElements)
put(newElement, pos);
else {
numElements++;
// Allocated memory has to be increased.
- if(numElements >= numAlloc) {
+ if (numElements >= numAlloc) {
numAlloc *= 2;
array = (Object**)realloc(array, numAlloc * sizeof(Object*));
}
- for(int i = numElements - 1; i > pos; i--)
+ for (int i = numElements - 1; i > pos; i--)
array[i] = array[i - 1];
array[pos] = newElement;
@@ -176,10 +175,10 @@ void Vector::insert(Object *newElement, int pos)
void Vector::remove(int pos)
{
- if(ownerOfObjects && array[pos])
+ if (ownerOfObjects && array[pos])
delete array[pos];
- for(int i = pos + 1; i < numElements; i++)
+ for (int i = pos + 1; i < numElements; i++)
array[i - 1] = array[i];
numElements--;
@@ -220,8 +219,8 @@ List::~List()
void List::clear()
{
- while(first) {
- if(ownerOfObjects && first->object)
+ while (first) {
+ if (ownerOfObjects && first->object)
delete first->object;
Node *next = first->next;
delete first;
@@ -238,10 +237,10 @@ void List::append(Object *element)
newLast->next = NULL;
newLast->object = element;
- if(last) {
+ if (last) {
last->next = newLast;
last = newLast;
- } else
+ } else
first = last = newLast;
numElements++;
@@ -252,21 +251,21 @@ bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll)
{
Node *beforeCur, *cur;
- for(beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
+ for (beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
if (compare ?
(cur->object && element->equals(cur->object)) :
element == cur->object) {
- if(beforeCur) {
+ if (beforeCur) {
beforeCur->next = cur->next;
- if(cur->next == NULL)
+ if (cur->next == NULL)
last = beforeCur;
} else {
first = cur->next;
- if(first == NULL)
+ if (first == NULL)
last = NULL;
}
- if(ownerOfObjects && cur->object && !doNotDeleteAtAll)
+ if (ownerOfObjects && cur->object && !doNotDeleteAtAll)
delete cur->object;
delete cur;
@@ -282,7 +281,7 @@ Object *List::ListIterator::getNext()
{
Object *object;
- if(current) {
+ if (current) {
object = current->object;
current = current->next;
} else
@@ -313,20 +312,20 @@ HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize)
this->tableSize = tableSize;
table = new Node*[tableSize];
- for(int i = 0; i < tableSize; i++)
+ for (int i = 0; i < tableSize; i++)
table[i] = NULL;
}
HashTable::~HashTable()
{
- for(int i = 0; i < tableSize; i++) {
+ for (int i = 0; i < tableSize; i++) {
Node *n1 = table[i];
- while(n1) {
+ while (n1) {
Node *n2 = n1->next;
- if(ownerOfValues && n1->value)
+ if (ownerOfValues && n1->value)
delete n1->value;
- if(ownerOfKeys)
+ if (ownerOfKeys)
delete n1->key;
delete n1;
@@ -342,9 +341,9 @@ void HashTable::intoStringBuffer(misc::StringBuffer *sb)
sb->append("{ ");
bool first = true;
- for(int i = 0; i < tableSize; i++) {
- for(Node *node = table[i]; node; node = node->next) {
- if(!first)
+ for (int i = 0; i < tableSize; i++) {
+ for (Node *node = table[i]; node; node = node->next) {
+ if (!first)
sb->append(", ");
node->key->intoStringBuffer(sb);
sb->append(" => ");
@@ -369,7 +368,7 @@ void HashTable::put(Object *key, Object *value)
bool HashTable::contains(Object *key)
{
int h = calcHashValue(key);
- for(Node *n = table[h]; n; n = n->next) {
+ for (Node *n = table[h]; n; n = n->next) {
if (key->equals(n->key))
return true;
}
@@ -380,7 +379,7 @@ bool HashTable::contains(Object *key)
Object *HashTable::get(Object *key)
{
int h = calcHashValue(key);
- for(Node *n = table[h]; n; n = n->next) {
+ for (Node *n = table[h]; n; n = n->next) {
if (key->equals(n->key))
return n->value;
}
@@ -393,16 +392,16 @@ bool HashTable::remove(Object *key)
int h = calcHashValue(key);
Node *last, *cur;
- for(last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) {
+ for (last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) {
if (key->equals(cur->key)) {
- if(last)
+ if (last)
last->next = cur->next;
else
table[h] = cur->next;
- if(ownerOfValues && cur->value)
+ if (ownerOfValues && cur->value)
delete cur->value;
- if(ownerOfKeys)
+ if (ownerOfKeys)
delete cur->key;
delete cur;
@@ -416,7 +415,7 @@ bool HashTable::remove(Object *key)
Object *HashTable::getKey (Object *key)
{
int h = calcHashValue(key);
- for(Node *n = table[h]; n; n = n->next) {
+ for (Node *n = table[h]; n; n = n->next) {
if (key->equals(n->key))
return n->key;
}
@@ -434,11 +433,11 @@ HashTable::HashTableIterator::HashTableIterator(HashTable *table)
void HashTable::HashTableIterator::gotoNext()
{
- if(node)
+ if (node)
node = node->next;
- while(node == NULL) {
- if(pos >= table->tableSize - 1)
+ while (node == NULL) {
+ if (pos >= table->tableSize - 1)
return;
pos++;
node = table->table[pos];
@@ -449,7 +448,7 @@ void HashTable::HashTableIterator::gotoNext()
Object *HashTable::HashTableIterator::getNext()
{
Object *result;
- if(node)
+ if (node)
result = node->key;
else
result = NULL;
@@ -492,7 +491,7 @@ void Stack::push (object::Object *object)
newTop->prev = top;
top = newTop;
- if(bottom == NULL)
+ if (bottom == NULL)
bottom = top;
numElements++;
@@ -503,11 +502,11 @@ void Stack::pushUnder (object::Object *object)
Node *newBottom = new Node ();
newBottom->object = object;
newBottom->prev = NULL;
- if(bottom != NULL)
+ if (bottom != NULL)
bottom->prev = newBottom;
bottom = newBottom;
- if(top == NULL)
+ if (top == NULL)
top = bottom;
numElements++;
@@ -522,7 +521,7 @@ void Stack::pop ()
delete top;
top = newTop;
- if(top == NULL)
+ if (top == NULL)
bottom = NULL;
numElements--;
@@ -532,7 +531,7 @@ Object *Stack::StackIterator::getNext()
{
Object *object;
- if(current) {
+ if (current) {
object = current->object;
current = current->prev;
} else
diff --git a/lout/container.hh b/lout/container.hh
index 26803e23..68a15da4 100644
--- a/lout/container.hh
+++ b/lout/container.hh
@@ -8,7 +8,7 @@
* members are instances of object::Object.
*
* A common problem in languanges without garbage collection is, where the
- * children belong to, and so, who is responsible to delete them (instanciation
+ * children belong to, and so, who is responsible to delete them (instantiation
* is always done by the caller). This information is here told to the
* collections, each container has a constructor with the parameter
* "ownerOfObjects" (HashTable has two such parameters, for keys and values).
@@ -20,7 +20,7 @@ namespace lout {
namespace container {
/**
- * \brief The container classes defined here contain instances of
+ * \brief The container classes defined here contain instances of
* object::Object.
*
* Different sub-classes may be mixed, and you have to care about casting,
@@ -44,13 +44,13 @@ protected:
{
private:
int refcount;
-
+
public:
AbstractIterator() { refcount = 1; }
-
+
void ref () { refcount++; }
void unref () { refcount--; if (refcount == 0) delete this; }
-
+
virtual bool hasNext () = 0;
virtual Object *getNext () = 0;
};
@@ -60,7 +60,7 @@ protected:
};
/**
- * \brief This is a small wrapper for AbstractIterator, which may be used
+ * \brief This is a small wrapper for AbstractIterator, which may be used
* directly, not as a pointer, to makes memory management simpler.
*/
class Iterator
@@ -70,7 +70,7 @@ class Iterator
private:
Collection0::AbstractIterator *impl;
- // Should not instanciated directly.
+ // Should not instantiated directly.
inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; }
public:
@@ -271,7 +271,7 @@ protected:
public:
Stack (bool ownerOfObjects);
~Stack();
-
+
void push (object::Object *object);
void pushUnder (object::Object *object);
inline object::Object *getTop () { return top ? top->object : NULL; }
@@ -421,7 +421,7 @@ public:
inline Stack (bool ownerOfObjects)
{ this->base = new untyped::Stack (ownerOfObjects); }
~Stack() { delete this->base; }
-
+
inline void push (T *object) {
((untyped::Stack*)this->base)->push (object); }
inline void pushUnder (T *object)
diff --git a/lout/identity.cc b/lout/identity.cc
index 8a5d68fd..ebe95ef0 100644
--- a/lout/identity.cc
+++ b/lout/identity.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "identity.hh"
diff --git a/lout/identity.hh b/lout/identity.hh
index 7dcdbac4..6102933d 100644
--- a/lout/identity.hh
+++ b/lout/identity.hh
@@ -53,7 +53,7 @@ namespace identity {
* </ul>
*
* After this, <i>class</i>::CLASS_ID refers to a number, which denotes the
- * class. (If this is still -1, since the class has not yet been instanciated,
+ * class. (If this is still -1, since the class has not yet been instantiated,
* any test will fail, which is correct.)
*
* <h3>Notes on implementation</h3>
@@ -131,11 +131,11 @@ public:
* identity::IdentifiableObject::instanceOf are done.
*/
int getClassId () { return classId; }
-
+
/**
* \brief Return the name, under which the class of this object was
* registered.
- */
+ */
const char *getClassName() { return classesById->get(classId)->className; }
bool instanceOf (int otherClassId);
diff --git a/lout/misc.cc b/lout/misc.cc
index 2008737b..78f758fd 100644
--- a/lout/misc.cc
+++ b/lout/misc.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -38,29 +37,6 @@ void init (int argc, char *argv[])
prgName = strdup (argv[0]);
}
-void chop (char *s)
-{
- char *p = s + strlen (s) - 1;
- while (*p == '\n') {
- *p = 0;
- p--;
- }
-}
-
-char *strip (char *s)
-{
- while (isspace (*s))
- s++;
-
- char *p = s + strlen (s) - 1;
- while (isspace (*p)) {
- *p = 0;
- p--;
- }
-
- return s;
-}
-
// ----------------
// Comparable
// ----------------
@@ -77,11 +53,11 @@ int Comparable::compareFun(const void *p1, const void *p2)
{
Comparable **c1 = (Comparable**)p1;
Comparable **c2 = (Comparable**)p2;
- if(c1 && c2)
+ if (c1 && c2)
return ((*c1)->compareTo(*c2));
- else if(c1)
+ else if (c1)
return 1;
- else if(c2)
+ else if (c2)
return -1;
else
return 0;
@@ -104,7 +80,7 @@ StringBuffer::StringBuffer()
StringBuffer::~StringBuffer()
{
clear ();
- if(str)
+ if (str)
delete[] str;
}
@@ -121,7 +97,7 @@ void StringBuffer::appendNoCopy(char *str)
node->data = str;
node->next = NULL;
- if(firstNode == NULL) {
+ if (firstNode == NULL) {
firstNode = node;
lastNode = node;
} else {
@@ -141,20 +117,20 @@ void StringBuffer::appendNoCopy(char *str)
*/
const char *StringBuffer::getChars()
{
- if(strValid)
+ if (strValid)
return str;
- if(str)
+ if (str)
delete[] str;
str = new char[numChars + 1];
char *p = str;
- for(Node *node = firstNode; node; node = node->next) {
+ for (Node *node = firstNode; node; node = node->next) {
int l = strlen(node->data);
memcpy(p, node->data, l * sizeof(char));
p += l;
}
-
+
*p = 0;
strValid = true;
return str;
@@ -166,7 +142,7 @@ const char *StringBuffer::getChars()
void StringBuffer::clear ()
{
Node *node, *nextNode;
- for(node = firstNode; node; node = nextNode) {
+ for (node = firstNode; node; node = nextNode) {
nextNode = node->next;
delete node->data;
delete node;
@@ -183,7 +159,7 @@ void StringBuffer::clear ()
BitSet::BitSet(int initBits)
{
- numBytes = bytesForBits(initBits);
+ numBytes = bytesForBits(initBits);
bits = (unsigned char*)malloc(numBytes * sizeof(unsigned char));
clear();
}
@@ -196,14 +172,14 @@ BitSet::~BitSet()
void BitSet::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append("[");
- for(int i = 0; i < numBytes; i++)
+ for (int i = 0; i < numBytes; i++)
sb->append(get(i) ? "1" : "0");
sb->append("]");
}
bool BitSet::get(int i)
{
- if(8 * i >= numBytes)
+ if (8 * i >= numBytes)
return false;
else
return bits[i / 8] & (1 << (i % 8));
@@ -211,17 +187,17 @@ bool BitSet::get(int i)
void BitSet::set(int i, bool val)
{
- if(8 * i >= numBytes) {
+ if (8 * i >= numBytes) {
int newNumBytes = numBytes;
- while(8 * i >= newNumBytes)
+ while (8 * i >= newNumBytes)
newNumBytes *= 2;
bits =
(unsigned char*)realloc(bits, newNumBytes * sizeof(unsigned char));
memset(bits + numBytes, 0, newNumBytes - numBytes);
numBytes = newNumBytes;
}
-
- if(val)
+
+ if (val)
bits[i / 8] |= (1 << (i % 8));
else
bits[i / 8] &= ~(1 << (i % 8));
diff --git a/lout/misc.hh b/lout/misc.hh
index f9184bf3..393bac0c 100644
--- a/lout/misc.hh
+++ b/lout/misc.hh
@@ -31,9 +31,6 @@ template <class T> inline T max (T a, T b, T c)
extern const char *prgName;
void init (int argc, char *argv[]);
-void chop (char *s);
-char *strip (char *s);
-
inline void assertNotReached ()
{
@@ -51,7 +48,7 @@ class Comparable
{
public:
virtual ~Comparable();
-
+
/**
* \brief Compare two objects c1 and c2.
*
@@ -77,19 +74,12 @@ public:
template <class T> class SimpleVector
{
private:
- enum {
- /**
- * \brief Edit this for debugging. Should be optimized by the compiler.
- */
- BOUND_CHECKING = 1
- };
-
T *array;
int num, numAlloc;
inline void resize ()
{
- /* This algorithm was tunned for memory&speed with this huge page:
+ /* This algorithm was tuned for memory&speed with this huge page:
* http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz
*/
if (array == NULL) {
@@ -112,6 +102,14 @@ public:
this->array = NULL;
}
+ inline SimpleVector (const SimpleVector &o) {
+ this->array = NULL;
+ this->num = o.num;
+ this->numAlloc = o.numAlloc;
+ resize ();
+ memcpy (this->array, o.array, sizeof (T) * num);
+ }
+
inline ~SimpleVector ()
{
if (this->array)
@@ -130,19 +128,23 @@ public:
*
* May be necessary before calling misc::SimpleVector::set.
*/
- inline void increase() { this->num++; this->resize (); }
+ inline void increase() { setSize(this->num + 1); }
/**
- * \brief Set the size explicitely.
+ * \brief Set the size explicitly.
*
- * May be necessary before called before misc::SimpleVector::set.
+ * May be necessary before calling misc::SimpleVector::set.
*/
- inline void setSize(int newSize) { this->num = newSize; this->resize (); }
+ inline void setSize(int newSize) {
+ assert (newSize >= 0);
+ this->num = newSize;
+ this->resize ();
+ }
/**
- * \brief Set the size explicitely and initialize new values.
+ * \brief Set the size explicitly and initialize new values.
*
- * May be necessary before called before misc::SimpleVector::set.
+ * May be necessary before calling misc::SimpleVector::set.
*/
inline void setSize (int newSize, T t) {
int oldSize = this->num;
@@ -157,20 +159,18 @@ public:
* \sa misc::SimpleVector::get
*/
inline T* getRef (int i) {
- if (BOUND_CHECKING)
- assert (i >= 0 && i < this->num);
+ assert (i >= 0 && this->num - i > 0);
return array + i;
}
/**
- * \brief Return the one element, explicitety.
+ * \brief Return the one element, explicitly.
*
* The element is copied, so for complex elements, you should rather used
* misc::SimpleVector::getRef.
*/
inline T get (int i) {
- if (BOUND_CHECKING)
- assert (i >= 0 && i < this->num);
+ assert (i >= 0 && this->num - i > 0);
return this->array[i];
}
@@ -183,8 +183,7 @@ public:
* be necessary before.
*/
inline void set (int i, T t) {
- if (BOUND_CHECKING)
- assert (i >= 0 && i < this->num);
+ assert (i >= 0 && this->num - i > 0);
this->array[i] = t;
}
};
@@ -232,9 +231,9 @@ class BitSet
private:
unsigned char *bits;
int numBytes;
-
+
inline int bytesForBits(int bits) { return bits == 0 ? 1 : (bits + 7) / 8; }
-
+
public:
BitSet(int initBits);
~BitSet();
@@ -255,7 +254,7 @@ private:
size_t poolSize, poolLimit, freeIdx;
SimpleVector <char*> *pools;
SimpleVector <char*> *bulk;
-
+
public:
ZoneAllocator (size_t poolSize) {
this->poolSize = poolSize;
diff --git a/lout/msg.h b/lout/msg.h
new file mode 100644
index 00000000..4993c105
--- /dev/null
+++ b/lout/msg.h
@@ -0,0 +1,39 @@
+#ifndef __MSG_H__
+#define __MSG_H__
+
+#include <stdio.h>
+
+#define prefs_show_msg 1
+
+#define D_STMT_START do
+#define D_STMT_END while (0)
+
+/*
+ * You can disable any MSG* macro by adding the '_' prefix.
+ */
+#define _MSG(...)
+#define _MSG_WARN(...)
+#define _MSG_ERR(...)
+
+
+#define MSG(...) \
+ D_STMT_START { \
+ if (prefs_show_msg){ \
+ printf(__VA_ARGS__); \
+ fflush (stdout); \
+ } \
+ } D_STMT_END
+
+#define MSG_WARN(...) \
+ D_STMT_START { \
+ if (prefs_show_msg) \
+ printf("** WARNING **: " __VA_ARGS__); \
+ } D_STMT_END
+
+#define MSG_ERR(...) \
+ D_STMT_START { \
+ if (prefs_show_msg) \
+ printf("** ERROR **: " __VA_ARGS__); \
+ } D_STMT_END
+
+#endif /* __MSG_H__ */
diff --git a/lout/object.cc b/lout/object.cc
index 7da124fa..9eec028e 100644
--- a/lout/object.cc
+++ b/lout/object.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -186,11 +185,11 @@ int ConstString::hashValue()
int ConstString::compareTo(Comparable *other)
{
String *otherString = (String*)other;
- if(str && otherString->str)
+ if (str && otherString->str)
return strcmp(str, otherString->str);
- else if(str)
+ else if (str)
return 1;
- else if(otherString->str)
+ else if (otherString->str)
return -1;
else
return 0;
@@ -199,7 +198,7 @@ int ConstString::compareTo(Comparable *other)
int ConstString::hashValue(const char *str)
{
- if(str) {
+ if (str) {
int h = 0;
for (int i = 0; str[i]; i++)
h = (h * 256 + str[i]);
@@ -223,7 +222,7 @@ String::String (const char *str): ConstString (str ? strdup(str) : NULL)
String::~String ()
{
- if(str)
+ if (str)
delete str;
}
@@ -239,9 +238,9 @@ PairBase::PairBase(Object *first, Object *second)
PairBase::~PairBase()
{
- if(first)
+ if (first)
delete first;
- if(second)
+ if (second)
delete second;
}
@@ -251,7 +250,7 @@ bool PairBase::equals(Object *other)
return
// Identical?
- this == other ||
+ this == other || (
(// Both first parts are NULL, ...
(first == NULL && otherPair->first == NULL) ||
// ... or both first parts are not NULL and equal
@@ -260,18 +259,18 @@ bool PairBase::equals(Object *other)
// Same with second part.
((second == NULL && otherPair->second == NULL) ||
(second != NULL && otherPair->second != NULL
- && second->equals (otherPair->second)));
+ && second->equals (otherPair->second))));
}
int PairBase::hashValue()
{
int value = 0;
- if(first)
+ if (first)
value ^= first->hashValue();
- if(second)
+ if (second)
value ^= second->hashValue();
-
+
return value;
}
@@ -279,14 +278,14 @@ void PairBase::intoStringBuffer(misc::StringBuffer *sb)
{
sb->append("<pair: ");
- if(first)
+ if (first)
first->intoStringBuffer(sb);
else
sb->append("(nil)");
sb->append(",");
- if(second)
+ if (second)
second->intoStringBuffer(sb);
else
sb->append("(nil)");
@@ -298,9 +297,9 @@ size_t PairBase::sizeOf()
{
size_t size = 0;
- if(first)
+ if (first)
size += first->sizeOf();
- if(second)
+ if (second)
size += second->sizeOf();
return size;
diff --git a/lout/object.hh b/lout/object.hh
index 7d505c99..789542fe 100644
--- a/lout/object.hh
+++ b/lout/object.hh
@@ -40,7 +40,7 @@ class Pointer: public Object
{
private:
void *value;
-
+
public:
Pointer(void *value) { this->value = value; }
bool equals(Object *other);
@@ -93,9 +93,9 @@ public:
int hashValue();
int compareTo(Comparable *other);
void intoStringBuffer(misc::StringBuffer *sb);
-
+
inline const char *chars() { return str; }
-
+
static int hashValue(const char *str);
};
diff --git a/lout/signal.cc b/lout/signal.cc
index 46aae626..35c16fbb 100644
--- a/lout/signal.cc
+++ b/lout/signal.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -38,7 +37,7 @@ Emitter::Emitter ()
Emitter::~Emitter ()
{
- for(Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ for (Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) {
Receiver *receiver = it.getNext ();
receiver->unconnectFrom (this);
}
@@ -77,7 +76,7 @@ void Emitter::connect (Receiver *receiver)
*/
void Emitter::emitVoid (int signalNo, int argc, Object **argv)
{
- for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
Receiver *receiver = it.getNext();
emitToReceiver (receiver, signalNo, argc, argv);
}
@@ -93,7 +92,7 @@ bool Emitter::emitBool (int signalNo, int argc, Object **argv)
{
bool b = false, bt;
- for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ for (Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
Receiver *receiver = it.getNext();
// Note: All receivers are called, even if one returns true.
// Therefore, something like
@@ -118,7 +117,7 @@ Receiver::Receiver()
Receiver::~Receiver()
{
- for(Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) {
+ for (Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) {
Emitter *emitter = it.getNext();
emitter->unconnect (this);
}
diff --git a/lout/signal.hh b/lout/signal.hh
index c96247be..6b332203 100644
--- a/lout/signal.hh
+++ b/lout/signal.hh
@@ -23,7 +23,7 @@
*
* Typically, signals are grouped. To define a signal group \em bar for your
* class \em Foo, you have to define two classes, the emitter and the
- * receiver (BarEmitter and BarReceiver), and instanciate the emitter:
+ * receiver (BarEmitter and BarReceiver), and instantiate the emitter:
*
* \dot
* digraph G {
@@ -130,7 +130,7 @@
*
* <h4>Emitters</h4>
*
- * Emitters are typically instanciated one, for one object emitting the
+ * Emitters are typically instantiated one, for one object emitting the
* signals. In the example above, the class Foo will contain a field
* "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter").
*
@@ -160,7 +160,7 @@
* // ...
* };
* \endcode
- *
+ *
* The constructor of Qix should then set \em qix:
*
* \code
@@ -217,7 +217,7 @@ private:
container::typed::List <Receiver> *receivers;
void unconnect (Receiver *receiver);
-
+
protected:
void emitVoid (int signalNo, int argc, Object **argv);
bool emitBool (int signalNo, int argc, Object **argv);
diff --git a/src/IO/IO.c b/src/IO/IO.c
index a0e18226..4b0285f2 100644
--- a/src/IO/IO.c
+++ b/src/IO/IO.c
@@ -13,14 +13,9 @@
* Dillo's event driven IO engine
*/
-#include <stdio.h>
-#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <sys/socket.h>
#include "../msg.h"
#include "../chain.h"
#include "../klist.h"
@@ -64,13 +59,13 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* IO API - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
- * Return a newly created, and initialized, 'io' struct
+ * Return a new, initialized, 'io' struct
*/
-static IOData_t *IO_new(int op, int fd)
+static IOData_t *IO_new(int op)
{
IOData_t *io = dNew0(IOData_t, 1);
io->Op = op;
- io->FD = fd;
+ io->FD = -1;
io->Flags = 0;
io->Key = 0;
io->Buf = dStr_sized_new(IOBufLen);
@@ -138,7 +133,8 @@ static void IO_close_fd(IOData_t *io, int CloseCode)
/* With HTTP, if we close the writing part, the reading one also gets
* closed! (other clients may set 'IOFlag_ForceClose') */
- if ((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) {
+ if (((io->Flags & IOFlag_ForceClose) || (CloseCode == IO_StopRdWr)) &&
+ io->FD != -1) {
do
st = close(io->FD);
while (st < 0 && errno == EINTR);
@@ -152,10 +148,10 @@ static void IO_close_fd(IOData_t *io, int CloseCode)
/* Stop the polling on this FD */
if (CloseCode & IO_StopRd) {
events |= DIO_READ;
- }
+ }
if (CloseCode & IO_StopWr) {
events |= DIO_WRITE;
- }
+ }
a_IOwatch_remove_fd(io->FD, events);
_MSG(" end IO close (%d) <=====\n", io->FD);
}
@@ -314,6 +310,11 @@ static void IO_fd_write_cb(int fd, void *data)
*/
static void IO_submit(IOData_t *r_io)
{
+ if (r_io->FD < 0) {
+ MSG_ERR("IO_submit: FD not initialized\n");
+ return;
+ }
+
/* Insert this IO in ValidIOs */
IO_ins(r_io);
@@ -351,14 +352,18 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* Write data using select */
switch (Op) {
case OpStart:
- io = IO_new(IOWrite, *(int*)Data1); /* SockFD */
+ io = IO_new(IOWrite);
Info->LocalKey = io;
break;
case OpSend:
io = Info->LocalKey;
- dbuf = Data1;
- dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
- IO_submit(io);
+ if (Data2 && !strcmp(Data2, "FD")) {
+ io->FD = *(int*)Data1; /* SockFD */
+ } else {
+ dbuf = Data1;
+ dStr_append_l(io->Buf, dbuf->Buf, dbuf->Size);
+ IO_submit(io);
+ }
break;
case OpEnd:
case OpAbort:
@@ -376,7 +381,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Write-data status */
switch (Op) {
default:
@@ -390,10 +395,16 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* This part catches the reader's messages */
switch (Op) {
case OpStart:
- io = IO_new(IORead, *(int*)Data2); /* SockFD */
+ io = IO_new(IORead);
Info->LocalKey = io;
io->Info = Info;
- IO_submit(io);
+ break;
+ case OpSend:
+ io = Info->LocalKey;
+ if (Data2 && !strcmp(Data2, "FD")) {
+ io->FD = *(int*)Data1; /* SockFD */
+ IO_submit(io);
+ }
break;
case OpAbort:
io = Info->LocalKey;
@@ -405,7 +416,7 @@ void a_IO_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 2 FWD */
/* Send read-data */
io = Data1;
switch (Op) {
diff --git a/src/IO/IO.h b/src/IO/IO.h
index 65b032f5..b75488c2 100644
--- a/src/IO/IO.h
+++ b/src/IO/IO.h
@@ -1,9 +1,6 @@
#ifndef __IO_H__
#define __IO_H__
-#include <unistd.h>
-#include <sys/uio.h>
-
#include "d_size.h"
#include "../../dlib/dlib.h"
#include "../chain.h"
diff --git a/src/IO/Makefile.am b/src/IO/Makefile.am
index bc2dea7e..b168073c 100644
--- a/src/IO/Makefile.am
+++ b/src/IO/Makefile.am
@@ -1,3 +1,4 @@
+AM_CPPFLAGS=-DDILLO_BINDIR='"$(bindir)/"'
AM_CFLAGS = @LIBFLTK_CFLAGS@
AM_CXXFLAGS = @LIBFLTK_CXXFLAGS@
@@ -8,7 +9,6 @@ libDiof_a_SOURCES = \
mime.h \
about.c \
Url.h \
- proto.c \
http.c \
dpi.c \
IO.c \
diff --git a/src/IO/Url.h b/src/IO/Url.h
index 698bc6ad..95919f11 100644
--- a/src/IO/Url.h
+++ b/src/IO/Url.h
@@ -16,19 +16,19 @@ extern void a_Http_freeall(void);
int a_Http_init(void);
int a_Http_proxy_auth(void);
void a_Http_set_proxy_passwd(const char *str);
+char *a_Http_make_connect_str(const DilloUrl *url);
+const char *a_Http_get_proxy_urlstr();
Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy);
void a_Http_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
-void a_About_ccc(int Op, int Branch, int Dir, ChainLink *Info,
- void *Data1, void *Data2);
void a_IO_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
void a_Dpi_ccc (int Op, int Branch, int Dir, ChainLink *Info,
void *Data1, void *Data2);
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd);
-void a_Dpi_bye_dpid(void);
+void a_Dpi_dillo_exit(void);
void a_Dpi_init(void);
diff --git a/src/IO/about.c b/src/IO/about.c
index 4cd5e2c1..508bfd11 100644
--- a/src/IO/about.c
+++ b/src/IO/about.c
@@ -14,7 +14,7 @@
/*
* HTML text for startup screen
*/
-const char *AboutSplash=
+const char *const AboutSplash=
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html>\n"
"<head>\n"
@@ -75,7 +75,7 @@ const char *AboutSplash=
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
-" <a href='http://cvs.auriga.wearlab.de/cgi-bin/cvsweb.cgi/dillo2/ChangeLog?rev=HEAD;cvsroot=dillo'>\n"
+" <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>\n"
" ChangeLog</a>\n"
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
@@ -129,7 +129,7 @@ const char *AboutSplash=
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
-" <a href='http://www.voltairenet.org/'>VoltaireNet</a>\n"
+" <a href='http://www.voltairenet.org/en'>VoltaireNet</a>\n"
" <tr>\n"
" <td>&nbsp;&nbsp;\n"
" <td>\n"
@@ -191,7 +191,7 @@ const char *AboutSplash=
" <tr><td>&nbsp;&nbsp;\n"
" <td><a href='http://www.violence.de'>Peace&amp;Violence</a>\n"
" <tr><td>&nbsp;&nbsp;\n"
-" <td><a href='http://www.gnu.org/philosophy/right-to-read.html'>"
+" <td><a href='http://www.gnu.org/philosophy/right-to-read.html'>\n"
" Right to Read</a>\n"
" </table>\n"
" </table>\n"
@@ -217,7 +217,8 @@ const char *AboutSplash=
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'><tr><td>\n"
" <p>\n"
-" Dillo is Free Software in the terms of the GPL3.\n"
+" Dillo is Free Software under the terms of version 3 of the\n"
+" <a href='http://www.gnu.org/licenses/gpl.html'>GPL</a>.\n"
" This means you have four basic freedoms:\n"
" <ul>\n"
" <li>Freedom to use the program any way you see fit.\n"
@@ -225,10 +226,9 @@ const char *AboutSplash=
" <li>Freedom to make backup copies.\n"
" <li>Freedom to redistribute it.\n"
" </ul>\n"
-" The <a href='http://www.gnu.org/licenses/gpl.html'>GPL3</a>\n"
-" is the legal mechanism that gives you these freedoms.\n"
-" It also protects them from being taken away: any derivative work\n"
-" based on the program must be under the GPL3.<br>\n"
+" The GPL is the legal mechanism that gives you these freedoms.\n"
+" It also protects you from having them taken away: any derivative work\n"
+" based on the program must be under GPLv3 as well.<br>\n"
" </table>\n"
"</table>\n"
"</table>\n"
@@ -240,25 +240,19 @@ const char *AboutSplash=
"<tr>\n"
" <td bgcolor='#CCCCCC'>\n"
" <h4>Release overview</h4>\n"
-" October 14, 2008\n"
+" February 11, 2010\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
" <table border='0' cellspacing='0' cellpadding='5'>\n"
" <tr>\n"
" <td>\n"
"<p>\n"
-"This is a rewrite of dillo, using FLTK2, that comes\n"
-"with lots of improvements and fixes.\n"
+"This release features a major overhaul of the cookies subsystem,\n"
+"a reimplementation of the DPI API, a configurable connection limit,\n"
+"and various CSS improvements.\n"
"<p>\n"
-"Our users will surely enjoy this new release as it will give them\n"
-"the same things they're accustomed plus tabbed browsing,\n"
-"antialiasing, different\n"
-"character sets, accepting compressed pages, control over image\n"
-"loading, smaller footprint, fewer dependencies, better table\n"
-"rendering, bugfixes, improved GUI, ... In brief, a better dillo.\n"
-"<p>\n"
-"Remember that dillo project uses a release model where every new\n"
-"browser shall be better than the former.\n"
+"Remember that the dillo project uses a release model where every new\n"
+"version shall be better than the last.\n"
"<EM>Keep up with the latest one!</EM>\n"
" </table>\n"
"</table>\n"
@@ -272,7 +266,7 @@ const char *AboutSplash=
" <td bgcolor='#CCCCCC'>\n"
" <h4>ChangeLog highlights</h4>\n"
" (Extracted from the\n"
-" <a href='http://cvs.auriga.wearlab.de/cgi-bin/cvsweb.cgi/dillo2/ChangeLog?rev=HEAD;cvsroot=dillo'>full\n"
+" <a href='http://hg.dillo.org/dillo/file/tip/ChangeLog'>full\n"
" ChangeLog</a>)\n"
"<tr>\n"
" <td bgcolor='#FFFFFF'>\n"
@@ -280,61 +274,27 @@ const char *AboutSplash=
" <tr>\n"
" <td>\n"
"<ul>\n"
-"<li>Ported Dillo from GTK1 to FLTK2."
-"<li>Ported a susbstantial part of the code from C to C++ (FLTK2 is in C++)."
-"<li>Wrote a new library: Dlib. With 'Dlib' Dillo doesn't need glib anymore."
-"<li>Ported all the code to Dlib."
-"<li>Made Dillo's UI Control Panel resizable on-the-fly."
-"<li>Implemented a new, simpler, dillorc parser."
-"<li>Reimplemented the Concomitant Callback chains into a uniform scheme!"
-"<li>Removed threads from IO. Now it only uses select-based watches."
-"<li>Simplified http.c by reusing the new non-blocking writes in IO."
-"<li>Implemented Stop button to not only stop rendering but also networking."
-"<li>Bound Ctrl+Space to toggle fullscreen mode."
-"<li>Added a http_referer preference. See details in dillorc."
-"<li>CCC: added reentrancy control to the OpEnd and OpAbort operations."
-"<li>CCC: enhanced the debug function and implemented OpAbort for dpi."
-"<li>Hooked a decoder for text/plain with charset."
-"<li>Forbid dpi GET and POST from non dpi-generated urls."
-"<li>Implemented tabbed browsing."
-"<li>Added a image-loading toggle button to the UI."
-"<li>Added line numbers and enabled wrapping in the 'View Source' window."
-"<li>Added HTTP-1.1's chunked transfer support!"
-"<li>Made the stop button sensitive when loading an image."
-"<li>Added support for 'charset' in the HTTP header field for Content-Type."
-"<li>Added support for 'charset' in the META element."
-"<li>Added the multipart/form-data encoding method to form submission."
-"<li>Made zlib a configure requirement, and cleaned up configure.in."
-"<li>Enabled the file dpi to look inside gzipped files."
-"<li>Added code for optional image loading (nice interface)!"
-"<li>Fixed data guesser to detect ASCII, LATIN1, UTF8, KOI8-R, CP-1251 as"
-" text."
-"<li>Fixed void to int conversions for 64bit-arch."
-"<li>Set the url resolver to escape illegal chars instead of stripping."
-"<li>Big html.cc cleanup. New classes, form API, source split."
-"<li>Added int32_t, EAI_NODATA and iconv tests for FreeBSD."
-"<li>Replaced the findtext dialog with an in-window widget!"
-"</ul>\n"
-"Dw2:<br>\n"
-"<ul>\n"
-"<li>Enabled clipped redraws (avoids some flickering)."
-"<li>Added combination of drawing rectangles into a larger one."
-"<li>Made getWidgetAtPoint() a virtual method of widget and implemented a"
-" custom one for TextBlock, reducing CPU usage on pages full of links."
-"<li>Set FltkViewBase::draw to intersect with view area for expose."
-"<li>Added double buffering for partial redraws!"
-"<li>Reduced memory usage in 30% by reusing styles, reducing the size"
-" of struct Content, and not preallocating in SimpleVector. !"
-"<li>Moved highlighting information from struct Word into Textblock"
-" to save memory."
-"<li>Reduced memory usage 10% with a custom memory handler in Textblock."
-"<li>Implemented selection of multibyte glyphs (UTF-8)."
-"<li>Fixed a slithery BUG in lout::misc::Stringbuffer."
-"<li>Added 'enter' and 'leave' signals into class Resource."
-"<li>Enabled mouse wheel scrolling."
-"<li>Added setDeleteCallback(DW_Callback_t func, void *data) to widget."
-" This allows to hook a callback when the widget is destroyed."
-"<li>Changed the table-apportion algorithms + bug fixes. Big work!"
+"<li>Added keybindings for scrolling.\n"
+"<li>Help button and local help file.\n"
+"<li>Add support for multiple class names in CSS.\n"
+"<li>Fix X11 coordinate overflows.\n"
+"<li>Improve CSS font parsing.\n"
+"<li>Enable font face setting via &lt;font&gt; element.\n"
+"<li>Ignore XML comment markers in CSS.\n"
+"<li>Fix user agent style for nested &lt;ul&gt;.\n"
+"<li>Handle signed chars. Added dIsspace() and dIsalnum() to dlib.\n"
+"<li>Changed the CCCs to build in one step (for both HTTP and DPI).\n"
+"<li>Remove the empty cache entry lingering after connection abort.\n"
+"<li>Fixed URL unescaping in the datauri DPI.\n"
+"<li>Changed and reimplemented the DPI API.\n"
+"<li>Allow linebreaks around Chinese/Japanese characters.\n"
+"<li>Fix scrolling for text search.\n"
+"<li>Tooltips.\n"
+"<li>Enable popup menu below bottom of page content.\n"
+"<li>Handle JPEGs with CMYK color space.\n"
+"<li>General cookies overhaul.\n"
+"<li>Fixed a bug in w3c_mode.\n"
+"<li>Limit number of simultaneous connections.\n"
"</ul>\n"
" </table>\n"
"</table>\n"
@@ -355,20 +315,20 @@ const char *AboutSplash=
"<ul>\n"
" <li> There's a\n"
" <a href='http://www.dillo.org/dillorc'>dillorc</a>\n"
-" (readable config) file within the tarball; It is well commented\n"
+" (readable config) file within the tarball; It is well-commented\n"
" and has plenty of options to customize dillo, so <STRONG>copy\n"
" it</STRONG> to your <STRONG>~/.dillo/</STRONG> directory, and\n"
-" modify to your taste.\n"
-" <li> There's documentation for developers in the <CODE>/doc</CODE>\n"
+" modify it to your taste.\n"
+" <li> Documentation for developers is in the <CODE>/doc</CODE>\n"
" dir within the tarball; you can find directions on everything\n"
" else at the home page.\n"
-" <li> Dillo has context sensitive menus using the\n"
-" right mouse button (available on pages, links, images,\n"
-" the Back and Forward buttons, and bug meter).\n"
+" <li> The right mouse button brings up a context-sensitive menu\n"
+" (available on pages, links, images, forms, the Back and Forward buttons,\n"
+" and the bug meter).\n"
" <li> Dillo behaves very nicely when browsing local files, images, and HTML.\n"
" It's also very good for Internet searching.\n"
-" <li> This release is mainly intended <strong>for developers</strong>\n"
-" and <em>advanced users</em>.\n"
+" <li> This release is mainly intended for <strong>developers</strong>\n"
+" and <strong>advanced users</strong>.\n"
" <li> Frames, Java and Javascript are not supported.\n"
"</ul>\n"
"<br>\n"
diff --git a/src/IO/dpi.c b/src/IO/dpi.c
index 7c4357da..6f46b2ba 100644
--- a/src/IO/dpi.c
+++ b/src/IO/dpi.c
@@ -21,16 +21,16 @@
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h> /* for errno */
+#include <fcntl.h>
+#include <ctype.h> /* isxdigit */
-#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
+#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -75,7 +75,7 @@ typedef struct {
*/
static Klist_t *ValidConns = NULL; /* Active connections list. It holds
* pointers to dpi_conn_t structures. */
-
+static char SharedKey[32];
/*
* Initialize local data
@@ -92,6 +92,7 @@ static void Dpi_close_fd(int fd)
{
int st;
+ dReturn_if (fd < 0);
do
st = close(fd);
while (st < 0 && errno == EINTR);
@@ -224,14 +225,14 @@ static void Dpi_parse_token(dpi_conn_t *conn)
tag = dStrndup(Tok, (size_t)conn->TokSize);
_MSG("Dpi_parse_token: {%s}\n", tag);
- cmd = a_Dpip_get_attr(Tok, conn->TokSize, "cmd");
+ cmd = a_Dpip_get_attr_l(Tok, conn->TokSize, "cmd");
if (strcmp(cmd, "send_status_message") == 0) {
- msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
dFree(msg);
} else if (strcmp(cmd, "chat") == 0) {
- msg = a_Dpip_get_attr(Tok, conn->TokSize, "msg");
+ msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
dFree(msg);
@@ -241,13 +242,13 @@ static void Dpi_parse_token(dpi_conn_t *conn)
} else if (strcmp(cmd, "start_send_page") == 0) {
conn->Send2EOF = 1;
- urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
dFree(urlstr);
- /* TODO: a_Dpip_get_attr(Tok, conn->TokSize, "send_mode") */
+ /* TODO: a_Dpip_get_attr_l(Tok, conn->TokSize, "send_mode") */
} else if (strcmp(cmd, "reload_request") == 0) {
- urlstr = a_Dpip_get_attr(Tok, conn->TokSize, "url");
+ urlstr = a_Dpip_get_attr_l(Tok, conn->TokSize, "url");
a_Chain_fcb(OpSend, conn->InfoRecv, urlstr, cmd);
dFree(urlstr);
}
@@ -260,6 +261,66 @@ static void Dpi_parse_token(dpi_conn_t *conn)
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
+ * Write data into a file descriptor taking care of EINTR
+ * and possible data splits.
+ * Return value: 1 on success, -1 on error.
+ */
+static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
+{
+ int st, sent = 0;
+
+ while (sent < msg_len) {
+ st = write(fd, msg + sent, msg_len - sent);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
+ break;
+ }
+ }
+ sent += st;
+ }
+
+ return (sent == msg_len) ? 1 : -1;
+}
+
+/*
+ * Read all the available data from a filedescriptor.
+ * This is intended for short answers, i.e. when we know the server
+ * will write it all before being preempted. For answers that may come
+ * as an stream with delays, non-blocking is better.
+ * Return value: read data, or NULL on error and no data.
+ */
+static char *Dpi_blocking_read(int fd)
+{
+ int st;
+ const int buf_sz = 8*1024;
+ char buf[buf_sz], *msg = NULL;
+ Dstr *dstr = dStr_sized_new(buf_sz);
+
+ do {
+ st = read(fd, buf, buf_sz);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
+ break;
+ }
+ } else if (st > 0) {
+ dStr_append_l(dstr, buf, st);
+ }
+ } while (st == buf_sz);
+
+ msg = (dstr->len > 0) ? dstr->str : NULL;
+ dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
+ return msg;
+}
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/*
* Get a new data buffer (within a 'dbuf'), save it into local data,
* split in tokens and parse the contents.
*/
@@ -290,8 +351,8 @@ static void Dpi_process_dbuf(int Op, void *Data1, dpi_conn_t *conn)
static int Dpi_start_dpid(void)
{
pid_t pid;
- int st_pipe[2], n, ret = 1;
- char buf[16];
+ int st_pipe[2], ret = 1;
+ char *answer;
/* create a pipe to track our child's status */
if (pipe(st_pipe))
@@ -302,15 +363,19 @@ static int Dpi_start_dpid(void)
/* This is the child process. Execute the command. */
char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
Dpi_close_fd(st_pipe[0]);
- if (execl(path1, "dpid", NULL) == -1) {
+ if (execl(path1, "dpid", (char*)NULL) == -1) {
dFree(path1);
- if (execlp("dpid", "dpid", NULL) == -1) {
- MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
- do
- n = write(st_pipe[1], "ERROR", 5);
- while (n == -1 && errno == EINTR);
- Dpi_close_fd(st_pipe[1]);
- _exit (EXIT_FAILURE);
+ path1 = dStrconcat(DILLO_BINDIR, "dpid", NULL);
+ if (execl(path1, "dpid", (char*)NULL) == -1) {
+ dFree(path1);
+ if (execlp("dpid", "dpid", (char*)NULL) == -1) {
+ MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
+ if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
+ MSG("Dpi_start_dpid (child): can't write to pipe.\n");
+ }
+ Dpi_close_fd(st_pipe[1]);
+ _exit (EXIT_FAILURE);
+ }
}
}
} else if (pid < 0) {
@@ -323,89 +388,90 @@ static int Dpi_start_dpid(void)
} else {
/* This is the parent process, check our child status... */
Dpi_close_fd(st_pipe[1]);
- do
- n = read(st_pipe[0], buf, 16);
- while (n == -1 && errno == EINTR);
- _MSG("Dpi_start_dpid: n = %d\n", n);
- if (n != 5) {
- ret = 0;
+ if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
+ MSG("Dpi_start_dpid: can't start dpid\n");
+ dFree(answer);
} else {
- MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
+ ret = 0;
}
+ Dpi_close_fd(st_pipe[0]);
}
return ret;
}
/*
- * Make a connection test for a UDS.
- * Return: 0 OK, 1 Not working.
+ * Read dpid's communication keys from its saved file.
+ * Return value: 1 on success, -1 on error.
*/
-static int Dpi_check_uds(char *uds_name)
+static int Dpi_read_comm_keys(int *port)
{
- struct sockaddr_un pun;
- int SockFD, ret = 1;
-
- if (access(uds_name, W_OK) == 0) {
- /* socket connection test */
- memset(&pun, 0, sizeof(struct sockaddr_un));
- pun.sun_family = AF_LOCAL;
- strncpy(pun.sun_path, uds_name, sizeof (pun.sun_path));
-
- if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1 ||
- connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
- MSG("Dpi_check_uds: %s %s\n", dStrerror(errno), uds_name);
- } else {
- Dpi_close_fd(SockFD);
- ret = 0;
- }
+ FILE *In;
+ char *fname, *rcline = NULL, *tail;
+ int i, ret = -1;
+
+ fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
+ if ((In = fopen(fname, "r")) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
+ } else if ((rcline = dGetline(In)) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
+ } else {
+ *port = strtol(rcline, &tail, 10);
+ for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
+ SharedKey[i] = tail[i+1];
+ SharedKey[i] = 0;
+ ret = 1;
}
+ if (In)
+ fclose(In);
+ dFree(rcline);
+ dFree(fname);
+
return ret;
}
/*
- * Return the directory where the UDS are in,
- * NULL if it can't be found.
+ * Return a socket file descriptor
*/
-static char *Dpi_get_dpid_uds_dir(void)
+static int Dpi_make_socket_fd()
{
- FILE *in;
- char *saved_name_filename; /* :) */
- char dpid_uds_dir[256], *p = NULL;
-
- saved_name_filename =
- dStrconcat(dGethomedir(), "/.dillo/dpi_socket_dir", NULL);
- in = fopen(saved_name_filename, "r");
- dFree(saved_name_filename);
-
- if (in != NULL) {
- fgets(dpid_uds_dir, 256, in);
- fclose(in);
- if ((p = strchr(dpid_uds_dir, '\n'))) {
- *p = 0;
- }
- if (access(dpid_uds_dir, F_OK) == 0) {
- p = dStrdup(dpid_uds_dir);
- _MSG("Dpi_get_dpid_uds_dir:: %s\n", p);
- }
- }
+ int fd, one = 1, ret = -1;
- _MSG("Dpi_get_dpid_uds_dir: %s \n", dStrerror(errno));
- return p;
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
+ /* avoid delays when sending small pieces of data */
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+ ret = fd;
+ }
+ return ret;
}
/*
- * Return the dpid's UDS name, NULL on failure.
+ * Make a connection test for a IDS.
+ * Return: 1 OK, -1 Not working.
*/
-static char *Dpi_get_dpid_uds_name(void)
+static int Dpi_check_dpid_ids()
{
- char *dpid_uds_dir, *dpid_uds_name = NULL;
-
- if ((dpid_uds_dir = Dpi_get_dpid_uds_dir()) != NULL)
- dpid_uds_name= dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
-
- dFree(dpid_uds_dir);
- return dpid_uds_name;
+ struct sockaddr_in sin;
+ const socklen_t sin_sz = sizeof(sin);
+ int sock_fd, dpid_port, ret = -1;
+
+ /* socket connection test */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
+ MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
+ } else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
+ } else {
+ Dpi_close_fd(sock_fd);
+ ret = 1;
+ }
+ }
+ return ret;
}
/*
@@ -415,20 +481,16 @@ static char *Dpi_get_dpid_uds_name(void)
static int Dpi_check_dpid(int num_tries)
{
static int starting = 0;
- char *dpid_uds_name;
int check_st = 1, ret = 2;
- if ((dpid_uds_name = Dpi_get_dpid_uds_name()))
- check_st = Dpi_check_uds(dpid_uds_name);
-
- _MSG("Dpi_check_dpid: dpid_uds_name=%s, check_st=%d\n",
- dpid_uds_name, check_st);
+ check_st = Dpi_check_dpid_ids();
+ _MSG("Dpi_check_dpid: check_st=%d\n", check_st);
- if (check_st == 0) {
+ if (check_st == 1) {
/* connection test with dpi server passed */
starting = 0;
ret = 0;
- } else if (!dpid_uds_name || check_st) {
+ } else {
if (!starting) {
/* start dpid */
if (Dpi_start_dpid() == 0) {
@@ -436,6 +498,7 @@ static int Dpi_check_dpid(int num_tries)
ret = 1;
}
} else if (++starting < num_tries) {
+ /* starting */
ret = 1;
} else {
/* we waited too much, report an error... */
@@ -443,7 +506,6 @@ static int Dpi_check_dpid(int num_tries)
}
}
- dFree(dpid_uds_name);
_MSG("Dpi_check_dpid:: %s\n",
(ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
return ret;
@@ -467,132 +529,137 @@ static int Dpi_blocking_start_dpid(void)
}
/*
- * Return the UDS name of a dpi server.
+ * Return the dpi server's port number, or -1 on error.
* (A query is sent to dpid and then its answer parsed)
* note: as the available servers and/or the dpi socket directory can
* change at any time, we'll ask each time. If someday we find
* that connecting each time significantly degrades performance,
* an optimized approach can be tried.
*/
-static char *Dpi_get_server_uds_name(const char *server_name)
+static int Dpi_get_server_port(const char *server_name)
{
- char *dpid_uds_dir, *dpid_uds_name = NULL,
- *server_uds_name = NULL;
- int st;
-
- dReturn_val_if_fail (server_name != NULL, NULL);
- _MSG("Dpi_get_server_uds_name:: server_name = [%s]\n", server_name);
-
- dpid_uds_dir = Dpi_get_dpid_uds_dir();
- if (dpid_uds_dir) {
- struct sockaddr_un dpid;
- int sock, req_sz, rdlen;
- char buf[128], *cmd, *request, *rply;
- size_t buflen;
-
- /* Get the server's uds name from dpid */
- sock = socket(AF_LOCAL, SOCK_STREAM, 0);
- dpid.sun_family = AF_LOCAL;
- dpid_uds_name = dStrconcat(dpid_uds_dir, "/", "dpid.srs", NULL);
- _MSG("dpid_uds_name = [%s]\n", dpid_uds_name);
- strncpy(dpid.sun_path, dpid_uds_name, sizeof(dpid.sun_path));
-
- if (connect(sock, (struct sockaddr *) &dpid, sizeof(dpid)) == -1)
- perror("connect");
- /* ask dpid to check the server plugin and send its UDS name back */
+ int sock_fd = -1, dpi_port = -1;
+ int dpid_port, ok = 0;
+ struct sockaddr_in sin;
+ char *cmd, *request, *rply = NULL, *port_str;
+ socklen_t sin_sz;
+
+ dReturn_val_if_fail (server_name != NULL, dpi_port);
+ _MSG("Dpi_get_server_port:: server_name = [%s]\n", server_name);
+
+ /* Read dpid's port from saved file */
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ ok = 1;
+ }
+ if (ok) {
+ /* Connect a socket with dpid */
+ ok = 0;
+ sin_sz = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
+ connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ }
+ if (ok) {
+ /* ask dpid to check the dpi and send its port number back */
+ ok = 0;
request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
_MSG("[%s]\n", request);
- do
- st = write(sock, request, strlen(request));
- while (st < 0 && errno == EINTR);
- if (st < 0 && errno != EINTR)
- perror("writing request");
- dFree(request);
- shutdown(sock, 1); /* signals no more writes to dpid */
+ if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ dFree(request);
+ }
+ if (ok) {
/* Get the reply */
- rply = NULL;
- buf[0] = '\0';
- buflen = sizeof(buf)/sizeof(buf[0]);
- for (req_sz = 0; (rdlen = read(sock, buf, buflen)) != 0;
- req_sz += rdlen) {
- if (rdlen == -1 && errno == EINTR)
- continue;
- if (rdlen == -1) {
- perror(" ** Dpi_get_server_uds_name **");
- break;
- }
- rply = dRealloc(rply, (uint_t)(req_sz + rdlen + 1));
- if (req_sz == 0)
- rply[0] = '\0';
- strncat(rply, buf, (size_t)rdlen);
+ ok = 0;
+ if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG("Dpi_get_server_port: can't read server port from dpid.\n");
+ } else {
+ ok = 1;
}
- Dpi_close_fd(sock);
- _MSG("rply = [%s]\n", rply);
-
+ }
+ if (ok) {
/* Parse reply */
- if (rdlen == 0 && rply) {
- cmd = a_Dpip_get_attr(rply, (int)strlen(rply), "cmd");
- if (strcmp(cmd, "send_data") == 0)
- server_uds_name = a_Dpip_get_attr(rply, (int)strlen(rply), "msg");
- dFree(cmd);
- dFree(rply);
+ ok = 0;
+ cmd = a_Dpip_get_attr(rply, "cmd");
+ if (strcmp(cmd, "send_data") == 0) {
+ port_str = a_Dpip_get_attr(rply, "msg");
+ _MSG("Dpi_get_server_port: rply=%s\n", rply);
+ _MSG("Dpi_get_server_port: port_str=%s\n", port_str);
+ dpi_port = strtol(port_str, NULL, 10);
+ dFree(port_str);
+ ok = 1;
}
+ dFree(cmd);
}
- dFree(dpid_uds_dir);
- dFree(dpid_uds_name);
- _MSG("Dpi_get_server_uds_name:: %s\n", server_uds_name);
- return server_uds_name;
+ dFree(rply);
+ Dpi_close_fd(sock_fd);
+
+ return ok ? dpi_port : -1;
}
/*
* Connect a socket to a dpi server and return the socket's FD.
- * We have to ask 'dpid' (dpi daemon) for the UDS of the target dpi server.
+ * We have to ask 'dpid' (dpi daemon) for the port of the target dpi server.
* Once we have it, then the proper file descriptor is returned (-1 on error).
*/
static int Dpi_connect_socket(const char *server_name, int retry)
{
- char *server_uds_name;
- struct sockaddr_un pun;
- int SockFD, err;
-
- /* Query dpid for the UDS name for this server */
- server_uds_name = Dpi_get_server_uds_name(server_name);
- _MSG("server_uds_name = [%s]\n", server_uds_name);
+ struct sockaddr_in sin;
+ int sock_fd, err, dpi_port, ret=-1;
+ char *cmd = NULL;
- if (access(server_uds_name, F_OK) != 0) {
- MSG("server socket was NOT found\n");
+ /* Query dpid for the port number for this server */
+ if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
+ _MSG("Dpi_connect_socket:: can't get port number for %s\n", server_name);
return -1;
}
+ _MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
/* connect with this server's socket */
- memset(&pun, 0, sizeof(struct sockaddr_un));
- pun.sun_family = AF_LOCAL;
- strncpy(pun.sun_path, server_uds_name, sizeof (pun.sun_path));
- dFree(server_uds_name);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpi_port);
- if ((SockFD = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
perror("[dpi::socket]");
- else if (connect(SockFD, (void*)&pun, D_SUN_LEN(&pun)) == -1) {
+ } else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
err = errno;
- SockFD = -1;
+ sock_fd = -1;
MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
if (retry) {
switch (err) {
case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
- /* the server may crash and its socket name survive */
- unlink(pun.sun_path);
- SockFD = Dpi_connect_socket(server_name, FALSE);
+ sock_fd = Dpi_connect_socket(server_name, FALSE);
break;
}
}
+
+ /* send authentication Key (the server closes sock_fd on error) */
+ } else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
+ MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
+ } else {
+ ret = sock_fd;
}
+ dFree(cmd);
- return SockFD;
+ return ret;
}
-
/*
* CCC function for the Dpi module
*/
@@ -616,13 +683,13 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*fd = SockFD;
Info->LocalKey = fd;
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 1, 1);
- a_Chain_bcb(OpStart, Info, Info->LocalKey, NULL);
- /* tell the capi to start the receiving branch */
- a_Chain_fcb(OpSend, Info, Info->LocalKey, "SockFD");
+ a_Chain_bcb(OpStart, Info, NULL, NULL);
}
}
if (st == 0 && SockFD != -1) {
+ a_Chain_bcb(OpSend, Info, &SockFD, "FD");
+ a_Chain_fcb(OpSend, Info, &SockFD, "FD");
a_Chain_fcb(OpSend, Info, NULL, "DpidOK");
} else {
MSG_ERR("dpi.c: can't start dpi daemon\n");
@@ -646,7 +713,7 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Send commands to dpi-server (status) */
switch (Op) {
case OpAbort:
@@ -676,7 +743,7 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* BCK */
+ } else { /* 2 BCK */
switch (Op) {
case OpStart:
conn = Dpi_conn_new(Info);
@@ -688,7 +755,12 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
a_Chain_link_new(Info, a_Dpi_ccc, BCK, a_IO_ccc, 2, 2);
- a_Chain_bcb(OpStart, Info, NULL, Data1); /* IORead, SockFD */
+ a_Chain_bcb(OpStart, Info, NULL, NULL); /* IORead */
+ break;
+ case OpSend:
+ if (Data2 && !strcmp(Data2, "FD")) {
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ }
break;
case OpAbort:
a_Chain_bcb(OpAbort, Info, NULL, NULL);
@@ -703,77 +775,38 @@ void a_Dpi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
}
-/*! Send DpiBye to dpid
- * Note: currently disabled. Maybe it'd be better to have a
- * dpid_idle_timeout variable in the config file.
+/*! Let dpid know dillo is no longer running.
+ * Note: currently disabled. It may serve to let the cookies dpi know
+ * when to expire session cookies.
*/
-void a_Dpi_bye_dpid()
+void a_Dpi_dillo_exit()
{
- char *DpiBye_cmd;
- struct sockaddr_un sa;
- size_t sun_path_len, addr_len;
- char *srs_name;
- int new_socket;
-
- srs_name = Dpi_get_dpid_uds_name();
- sun_path_len = sizeof(sa.sun_path);
- addr_len = sizeof(sa);
- sa.sun_family = AF_LOCAL;
-
- if ((new_socket = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
- MSG("a_Dpi_bye_dpid: %s\n", dStrerror(errno));
- }
- strncpy(sa.sun_path, srs_name, sizeof (sa.sun_path));
- if (connect(new_socket, (struct sockaddr *) &sa, addr_len) == -1) {
- MSG("a_Dpi_bye_dpid: %s\n", dStrerror(errno));
- MSG("%s\n", sa.sun_path);
- }
- DpiBye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
- (void) write(new_socket, DpiBye_cmd, strlen(DpiBye_cmd));
- dFree(DpiBye_cmd);
- Dpi_close_fd(new_socket);
}
-
/*
* Send a command to a dpi server, and block until the answer is got.
* Return value: the dpip tag answer as an string, NULL on error.
*/
char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
{
- int cst, SockFD;
- ssize_t st;
- char buf[16384], *retval = NULL;
+ int cst, sock_fd;
+ char *ret = NULL;
/* test the dpid, and wait a bit for it to start if necessary */
if ((cst = Dpi_blocking_start_dpid()) != 0) {
- return retval;
+ return ret;
}
- SockFD = Dpi_connect_socket(server_name, TRUE);
- if (SockFD != -1) {
- /* TODO: handle the case of (st < strlen(cmd)) */
- do
- st = write(SockFD, cmd, strlen(cmd));
- while (st == -1 && errno == EINTR);
-
- /* TODO: if the answer is too long... */
- do
- st = read(SockFD, buf, 16384);
- while (st < 0 && errno == EINTR);
-
- if (st == -1)
- perror("[a_Dpi_send_blocking_cmd]");
- else if (st > 0)
- retval = dStrndup(buf, (size_t)st);
-
- Dpi_close_fd(SockFD);
-
- } else {
- perror("[a_Dpi_send_blocking_cmd]");
+ if ((sock_fd = Dpi_connect_socket(server_name, TRUE)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
+ } if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
}
+ Dpi_close_fd(sock_fd);
- return retval;
+ return ret;
}
diff --git a/src/IO/http.c b/src/IO/http.c
index 03bb4522..77a1be43 100644
--- a/src/IO/http.c
+++ b/src/IO/http.c
@@ -16,12 +16,12 @@
#include <config.h>
+#include <ctype.h> /* isdigit */
#include <unistd.h>
#include <errno.h> /* for errno */
#include <stdlib.h>
-#include <signal.h>
#include <fcntl.h>
-#include <sys/wait.h>
+#include <assert.h>
#include <sys/socket.h> /* for lots of socket stuff */
#include <netinet/in.h> /* for ntohl and stuff */
#include <arpa/inet.h> /* for inet_ntop */
@@ -33,6 +33,7 @@
#include "../dns.h"
#include "../web.hh"
#include "../cookies.h"
+#include "../auth.h"
#include "../prefs.h"
#include "../misc.h"
@@ -47,34 +48,70 @@ D_STMT_START { \
#define _MSG_BW(web, root, ...)
+static const int HTTP_SOCKET_USE_PROXY = 0x1;
+static const int HTTP_SOCKET_QUEUED = 0x4;
+static const int HTTP_SOCKET_TO_BE_FREED = 0x8;
+
/* 'Url' and 'web' are just references (no need to deallocate them here). */
typedef struct {
int SockFD;
uint_t port; /* need a separate port in order to support PROXY */
- bool_t use_proxy; /* indicates whether to use proxy or not */
+ uint_t flags;
DilloWeb *web; /* reference to client's web structure */
Dlist *addr_list; /* Holds the DNS answer */
int Err; /* Holds the errno of the connect() call */
ChainLink *Info; /* Used for CCC asynchronous operations */
+ char *connected_to; /* Used for per-host connection limit */
} SocketData_t;
+/* Data structures and functions to queue sockets that need to be
+ * delayed due to the per host connection limit.
+ */
+typedef struct SocketQueueEntry {
+ SocketData_t* sock;
+ struct SocketQueueEntry *next ;
+} SocketQueueEntry_t;
+
+typedef struct {
+ SocketQueueEntry_t *head;
+ SocketQueueEntry_t *tail;
+} SocketQueue_t;
+
+typedef struct {
+ char *host;
+ int active_connections;
+ SocketQueue_t queue;
+} HostConnection_t;
+
+static void Http_socket_queue_init(SocketQueue_t *sq);
+static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock);
+static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq);
+static HostConnection_t *Http_host_connection_get(const char *host);
+static void Http_host_connection_remove(HostConnection_t *hc);
+static int Http_connect_socket(ChainLink *Info);
+static void Http_socket_free(int SKey);
/*
* Local data
*/
static Klist_t *ValidSocks = NULL; /* Active sockets list. It holds pointers to
* SocketData_t structures. */
-
static DilloUrl *HTTP_Proxy = NULL;
static char *HTTP_Proxy_Auth_base64 = NULL;
+static char *HTTP_Language_hdr = NULL;
+static Dlist *host_connections;
/*
- * Initialize proxy vars.
+ * Initialize proxy vars and Accept-Language header
*/
int a_Http_init(void)
{
char *env_proxy = getenv("http_proxy");
+ HTTP_Language_hdr = prefs.http_language ?
+ dStrconcat("Accept-Language: ", prefs.http_language, "\r\n", NULL) :
+ dStrdup("");
+
if (env_proxy && strlen(env_proxy))
HTTP_Proxy = a_Url_new(env_proxy, NULL);
if (!HTTP_Proxy && prefs.http_proxy)
@@ -86,6 +123,9 @@ int a_Http_init(void)
if (HTTP_Proxy && prefs.http_proxyuser && strchr(prefs.http_proxyuser, ':'))
HTTP_Proxy_Auth_base64 = a_Misc_encode_base64(prefs.http_proxyuser);
*/
+
+ host_connections = dList_new(5);
+
return 0;
}
@@ -118,6 +158,31 @@ static int Http_sock_new(void)
return a_Klist_insert(&ValidSocks, S);
}
+static void Http_connect_queued_sockets(HostConnection_t *hc)
+{
+ SocketData_t *sd;
+ while (hc->active_connections < prefs.http_max_conns &&
+ (sd = Http_socket_dequeue(&hc->queue))) {
+
+ sd->flags &= ~HTTP_SOCKET_QUEUED;
+
+ if (sd->flags & HTTP_SOCKET_TO_BE_FREED) {
+ dFree(sd);
+ } else if (a_Web_valid(sd->web)) {
+ /* start connecting the socket */
+ if (Http_connect_socket(sd->Info) < 0) {
+ MSG_BW(sd->web, 1, "ERROR: %s", dStrerror(sd->Err));
+ a_Chain_bfcb(OpAbort, sd->Info, NULL, "Both");
+ dFree(sd->Info);
+ Http_socket_free((int) sd->Info->LocalKey);
+ } else {
+ sd->connected_to = hc->host;
+ hc->active_connections++;
+ }
+ }
+ }
+}
+
/*
* Free SocketData_t struct
*/
@@ -127,7 +192,19 @@ static void Http_socket_free(int SKey)
if ((S = a_Klist_get_data(ValidSocks, SKey))) {
a_Klist_remove(ValidSocks, SKey);
- dFree(S);
+
+ if (S->flags & HTTP_SOCKET_QUEUED) {
+ S->flags |= HTTP_SOCKET_TO_BE_FREED;
+ } else {
+ if (S->connected_to) {
+ HostConnection_t *hc = Http_host_connection_get(S->connected_to);
+ hc->active_connections--;
+ Http_connect_queued_sockets(hc);
+ if (hc->active_connections == 0)
+ Http_host_connection_remove(hc);
+ }
+ dFree(S);
+ }
}
}
@@ -167,7 +244,7 @@ static char *Http_get_referer(const DilloUrl *url)
/*
* Generate Content-Type header value for a POST query.
*/
-Dstr *Http_make_content_type(const DilloUrl *url)
+static Dstr *Http_make_content_type(const DilloUrl *url)
{
Dstr *dstr;
@@ -196,16 +273,12 @@ Dstr *Http_make_content_type(const DilloUrl *url)
*/
Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
{
+ const char *auth;
char *ptr, *cookies, *referer;
- Dstr *s_port = dStr_new(""),
- *query = dStr_new(""),
+ Dstr *query = dStr_new(""),
*full_path = dStr_new(""),
*proxy_auth = dStr_new("");
- /* Sending the default port in the query may cause a 302-answer. --Jcid */
- if (URL_PORT(url) && URL_PORT(url) != DILLO_URL_HTTP_PORT)
- dStr_sprintfa(s_port, ":%d", URL_PORT(url));
-
if (use_proxy) {
dStr_sprintfa(full_path, "%s%s",
URL_STR(url),
@@ -224,6 +297,7 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
}
cookies = a_Cookies_get_query(url);
+ auth = a_Auth_get_auth_str(url);
referer = Http_get_referer(url);
if (URL_FLAGS(url) & URL_Post) {
Dstr *content_type = Http_make_content_type(url);
@@ -231,19 +305,22 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
query,
"POST %s HTTP/1.1\r\n"
"Connection: close\r\n"
+ "Accept: text/*,image/*,*/*;q=0.2\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
- "Host: %s%s\r\n"
+ "%s" /* language */
+ "%s" /* auth */
+ "Host: %s\r\n"
"%s"
"%s"
- "User-Agent: Dillo/%s\r\n"
+ "User-Agent: %s\r\n"
"Content-Length: %ld\r\n"
"Content-Type: %s\r\n"
- "%s"
+ "%s" /* cookies */
"\r\n",
- full_path->str, URL_HOST(url), s_port->str,
- proxy_auth->str, referer, VERSION,
- URL_DATA(url)->len, content_type->str,
+ full_path->str, HTTP_Language_hdr, auth ? auth : "",
+ URL_AUTHORITY(url), proxy_auth->str, referer, prefs.http_user_agent,
+ (long)URL_DATA(url)->len, content_type->str,
cookies);
dStr_append_l(query, URL_DATA(url)->str, URL_DATA(url)->len);
dStr_free(content_type, TRUE);
@@ -253,24 +330,26 @@ Dstr *a_Http_make_query_str(const DilloUrl *url, bool_t use_proxy)
"GET %s HTTP/1.1\r\n"
"%s"
"Connection: close\r\n"
+ "Accept: text/*,image/*,*/*;q=0.2\r\n"
"Accept-Charset: utf-8,*;q=0.8\r\n"
"Accept-Encoding: gzip\r\n"
- "Host: %s%s\r\n"
- "%s"
+ "%s" /* language */
+ "%s" /* auth */
+ "Host: %s\r\n"
"%s"
- "User-Agent: Dillo/%s\r\n"
"%s"
+ "User-Agent: %s\r\n"
+ "%s" /* cookies */
"\r\n",
full_path->str,
(URL_FLAGS(url) & URL_E2EQuery) ?
"Cache-Control: no-cache\r\nPragma: no-cache\r\n" : "",
- URL_HOST(url), s_port->str,
- proxy_auth->str, referer, VERSION, cookies);
+ HTTP_Language_hdr, auth ? auth : "", URL_AUTHORITY(url),
+ proxy_auth->str, referer, prefs.http_user_agent, cookies);
}
dFree(referer);
dFree(cookies);
- dStr_free(s_port, TRUE);
dStr_free(full_path, TRUE);
dStr_free(proxy_auth, TRUE);
_MSG("Query: {%s}\n", dStr_printable(query, 8192));
@@ -286,7 +365,7 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
DataBuf *dbuf;
/* Create the query */
- query = a_Http_make_query_str(S->web->url, S->use_proxy);
+ query = a_Http_make_query_str(S->web->url, S->flags & HTTP_SOCKET_USE_PROXY);
dbuf = a_Chain_dbuf_new(query->str, query->len, 0);
/* actually this message is sent too early.
@@ -294,14 +373,9 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
_MSG_BW(S->web, 1, "Sending query to %s...", URL_HOST_(S->web->url));
/* send query */
- a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
- a_Chain_bcb(OpStart, Info, &S->SockFD, NULL);
a_Chain_bcb(OpSend, Info, dbuf, NULL);
dFree(dbuf);
dStr_free(query, 1);
-
- /* Tell the cache to start the receiving CCC for the answer */
- a_Chain_fcb(OpSend, Info, &S->SockFD, "SockFD");
}
/*
@@ -311,7 +385,7 @@ static void Http_send_query(ChainLink *Info, SocketData_t *S)
*/
static int Http_connect_socket(ChainLink *Info)
{
- int status;
+ int i, status;
#ifdef ENABLE_IPV6
struct sockaddr_in6 name;
#else
@@ -324,61 +398,64 @@ static int Http_connect_socket(ChainLink *Info)
S = a_Klist_get_data(ValidSocks, VOIDP2INT(Info->LocalKey));
/* TODO: iterate this address list until success, or end-of-list */
- dh = dList_nth_data(S->addr_list, 0);
-
- if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
- S->Err = errno;
- MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
- return -1;
- }
- /* set NONBLOCKING and close on exec. */
- fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
- fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
-
- /* Some OSes require this... */
- memset(&name, 0, sizeof(name));
- /* Set remaining parms. */
- switch (dh->af) {
- case AF_INET:
- {
- struct sockaddr_in *sin = (struct sockaddr_in *)&name;
- socket_len = sizeof(struct sockaddr_in);
- sin->sin_family = dh->af;
- sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
- memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
- if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
- MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
- break;
- }
+ for (i = 0; (dh = dList_nth_data(S->addr_list, i)); ++i) {
+ if ((S->SockFD = socket(dh->af, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ S->Err = errno;
+ MSG("Http_connect_socket ERROR: %s\n", dStrerror(errno));
+ continue;
+ }
+ /* set NONBLOCKING and close on exec. */
+ fcntl(S->SockFD, F_SETFL, O_NONBLOCK | fcntl(S->SockFD, F_GETFL));
+ fcntl(S->SockFD, F_SETFD, FD_CLOEXEC | fcntl(S->SockFD, F_GETFD));
+
+ /* Some OSes require this... */
+ memset(&name, 0, sizeof(name));
+ /* Set remaining parms. */
+ switch (dh->af) {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&name;
+ socket_len = sizeof(struct sockaddr_in);
+ sin->sin_family = dh->af;
+ sin->sin_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin->sin_addr, dh->data, (size_t)dh->alen);
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ MSG("Connecting to %s\n", inet_ntoa(sin->sin_addr));
+ break;
+ }
#ifdef ENABLE_IPV6
- case AF_INET6:
- {
- char buf[128];
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
- socket_len = sizeof(struct sockaddr_in6);
- sin6->sin6_family = dh->af;
- sin6->sin6_port = S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
- memcpy(&sin6->sin6_addr, dh->data, dh->alen);
- inet_ntop(dh->af, dh->data, buf, sizeof(buf));
- if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
- MSG("Connecting to %s\n", buf);
- break;
- }
+ case AF_INET6:
+ {
+ char buf[128];
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&name;
+ socket_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = dh->af;
+ sin6->sin6_port =
+ S->port ? htons(S->port) : htons(DILLO_URL_HTTP_PORT);
+ memcpy(&sin6->sin6_addr, dh->data, dh->alen);
+ inet_ntop(dh->af, dh->data, buf, sizeof(buf));
+ if (a_Web_valid(S->web) && (S->web->flags & WEB_RootUrl))
+ MSG("Connecting to %s\n", buf);
+ break;
+ }
#endif
- }/*switch*/
-
- MSG_BW(S->web, 1, "Contacting host...");
- status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
- if (status == -1 && errno != EINPROGRESS) {
- S->Err = errno;
- Http_socket_close(S);
- MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
- return -1;
- } else {
- Http_send_query(S->Info, S);
+ }/*switch*/
+
+ MSG_BW(S->web, 1, "Contacting host...");
+ status = connect(S->SockFD, (struct sockaddr *)&name, socket_len);
+ if (status == -1 && errno != EINPROGRESS) {
+ S->Err = errno;
+ Http_socket_close(S);
+ MSG("Http_connect_socket ERROR: %s\n", dStrerror(S->Err));
+ } else {
+ a_Chain_bcb(OpSend, Info, &S->SockFD, "FD");
+ a_Chain_fcb(OpSend, Info, &S->SockFD, "FD");
+ Http_send_query(S->Info, S);
+ return 0; /* Success */
+ }
}
- return 0; /* Success */
+ return -1;
}
/*
@@ -393,9 +470,15 @@ static int Http_must_use_proxy(const DilloUrl *url)
if (HTTP_Proxy) {
ret = 1;
if (prefs.no_proxy) {
+ const char *host = URL_HOST(url);
+ size_t host_len = strlen(host);
+
np = dStrdup(prefs.no_proxy);
for (p = np; (tok = dStrsep(&p, " ")); ) {
- if (dStristr(URL_AUTHORITY(url), tok)) {
+ int start = host_len - strlen(tok);
+
+ if (start >= 0 && dStrcasecmp(host + start, tok) == 0) {
+ /* no_proxy token is suffix of host string */
ret = 0;
break;
}
@@ -403,42 +486,94 @@ static int Http_must_use_proxy(const DilloUrl *url)
dFree(np);
}
}
+ _MSG("Http_must_use_proxy: %s\n %s\n", URL_STR(url), ret ? "YES":"NO");
return ret;
}
/*
+ * Return a new string for the request used to tunnel HTTPS through a proxy.
+ * As of 2009, the best reference appears to be section 5 of RFC 2817.
+ */
+char *a_Http_make_connect_str(const DilloUrl *url)
+{
+ Dstr *dstr;
+ const char *auth1;
+ int auth_len;
+ char *auth2, *proxy_auth, *retstr;
+
+ dReturn_val_if_fail(Http_must_use_proxy(url), NULL);
+
+ dstr = dStr_new("");
+ auth1 = URL_AUTHORITY(url);
+ auth_len = strlen(auth1);
+ if (auth_len > 0 && !isdigit(auth1[auth_len - 1]))
+ /* if no port number, add HTTPS port */
+ auth2 = dStrconcat(auth1, ":443", NULL);
+ else
+ auth2 = dStrdup(auth1);
+ proxy_auth = HTTP_Proxy_Auth_base64 ?
+ dStrconcat ("Proxy-Authorization: Basic ",
+ HTTP_Proxy_Auth_base64, "\r\n", NULL) :
+ dStrdup("");
+ dStr_sprintfa(
+ dstr,
+ "CONNECT %s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "%s"
+ "\r\n",
+ auth2,
+ auth2,
+ proxy_auth);
+
+ dFree(auth2);
+ dFree(proxy_auth);
+ retstr = dstr->str;
+ dStr_free(dstr, 0);
+ return retstr;
+}
+
+/*
+ * Return URL string of HTTP proxy, if any
+ */
+const char *a_Http_get_proxy_urlstr()
+{
+ return HTTP_Proxy ? URL_STR(HTTP_Proxy) : NULL;
+}
+
+/*
* Callback function for the DNS resolver.
* Continue connecting the socket, or abort upon error condition.
* S->web is checked to assert the operation wasn't aborted while waiting.
*/
-void a_Http_dns_cb(int Status, Dlist *addr_list, void *data)
+static void Http_dns_cb(int Status, Dlist *addr_list, void *data)
{
int SKey = VOIDP2INT(data);
SocketData_t *S;
+ HostConnection_t *hc;
S = a_Klist_get_data(ValidSocks, SKey);
if (S) {
if (!a_Web_valid(S->web)) {
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
dFree(S->Info);
Http_socket_free(SKey);
} else if (Status == 0 && addr_list) {
/* Successful DNS answer; save the IP */
S->addr_list = addr_list;
- /* start connecting the socket */
- if (Http_connect_socket(S->Info) < 0) {
- MSG_BW(S->web, 1, "ERROR: %s", dStrerror(S->Err));
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
- dFree(S->Info);
- Http_socket_free(SKey);
- }
-
+ S->flags |= HTTP_SOCKET_QUEUED;
+ if (S->flags & HTTP_SOCKET_USE_PROXY)
+ hc = Http_host_connection_get(URL_HOST(HTTP_Proxy));
+ else
+ hc = Http_host_connection_get(URL_HOST(S->web->url));
+ Http_socket_enqueue(&hc->queue, S);
+ Http_connect_queued_sockets(hc);
} else {
/* DNS wasn't able to resolve the hostname */
MSG_BW(S->web, 0, "ERROR: Dns can't resolve %s",
- (S->use_proxy) ? URL_HOST_(HTTP_Proxy) : URL_HOST_(S->web->url));
- a_Chain_fcb(OpAbort, S->Info, NULL, NULL);
+ (S->flags & HTTP_SOCKET_USE_PROXY) ? URL_HOST_(HTTP_Proxy) :
+ URL_HOST_(S->web->url));
+ a_Chain_bfcb(OpAbort, S->Info, NULL, "Both");
dFree(S->Info);
Http_socket_free(SKey);
}
@@ -467,11 +602,11 @@ static int Http_get(ChainLink *Info, void *Data1)
if (Http_must_use_proxy(S->web->url)) {
hostname = dStrdup(URL_HOST(HTTP_Proxy));
S->port = URL_PORT(HTTP_Proxy);
- S->use_proxy = TRUE;
+ S->flags |= HTTP_SOCKET_USE_PROXY;
} else {
hostname = dStrdup(URL_HOST(S->web->url));
S->port = URL_PORT(S->web->url);
- S->use_proxy = FALSE;
+ S->flags &= ~HTTP_SOCKET_USE_PROXY;
}
/* Let the user know what we'll do */
@@ -479,7 +614,7 @@ static int Http_get(ChainLink *Info, void *Data1)
/* Let the DNS engine resolve the hostname, and when done,
* we'll try to connect the socket from the callback function */
- a_Dns_resolve(hostname, a_Http_dns_cb, Info->LocalKey);
+ a_Dns_resolve(hostname, Http_dns_cb, Info->LocalKey);
dFree(hostname);
return 0;
@@ -493,6 +628,8 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
{
int SKey = VOIDP2INT(Info->LocalKey);
+ (void)Data2; /* suppress unused parameter warning */
+
dReturn_if_fail( a_Chain_check("a_Http_ccc", Op, Branch, Dir, Info) );
if (Branch == 1) {
@@ -503,6 +640,10 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
/* ( Data1 = Web ) */
SKey = Http_sock_new();
Info->LocalKey = INT2VOIDP(SKey);
+ /* link IO */
+ a_Chain_link_new(Info, a_Http_ccc, BCK, a_IO_ccc, 1, 1);
+ a_Chain_bcb(OpStart, Info, NULL, NULL);
+ /* async. connection */
Http_get(Info, Data1);
break;
case OpEnd:
@@ -518,7 +659,7 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
dFree(Info);
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* HTTP send-query status branch */
switch (Op) {
default:
@@ -530,6 +671,82 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
}
+static void Http_socket_queue_init(SocketQueue_t *sq)
+{
+ sq->head = NULL;
+ sq->tail = NULL;
+}
+
+static void Http_socket_enqueue(SocketQueue_t *sq, SocketData_t* sock)
+{
+ SocketQueueEntry_t *se = dNew(SocketQueueEntry_t, 1);
+
+ se->sock = sock;
+ se->next = NULL;
+
+ if (sq->tail)
+ sq->tail->next = se;
+ sq->tail = se;
+
+ if (! sq->head)
+ sq->head = se;
+}
+
+static SocketData_t* Http_socket_dequeue(SocketQueue_t *sq)
+{
+ SocketQueueEntry_t *se = sq->head;
+ SocketData_t *sd = NULL;
+
+ if (se) {
+ sq->head = se->next;
+ if (sq->tail == se)
+ sq->tail = NULL;
+ sd = se->sock;
+ dFree(se);
+ }
+
+ return sd;
+}
+
+static HostConnection_t *Http_host_connection_get(const char *host)
+{
+ int i;
+ HostConnection_t *hc;
+
+ for (i = 0; i < dList_length(host_connections); i++) {
+ hc = (HostConnection_t*) dList_nth_data(host_connections, i);
+
+ if (dStrcasecmp(host, hc->host) == 0)
+ return hc;
+ }
+
+ hc = dNew0(HostConnection_t, 1);
+ Http_socket_queue_init(&hc->queue);
+ hc->host = dStrdup(host);
+ dList_append(host_connections, hc);
+
+ return hc;
+}
+
+static void Http_host_connection_remove(HostConnection_t *hc)
+{
+ assert(hc->queue.head == NULL);
+ dList_remove_fast(host_connections, hc);
+ dFree(hc->host);
+ dFree(hc);
+}
+
+static void Http_host_connection_remove_all()
+{
+ HostConnection_t *hc;
+
+ while (dList_length(host_connections) > 0) {
+ hc = (HostConnection_t*) dList_nth_data(host_connections, 0);
+ while (Http_socket_dequeue(&hc->queue));
+ Http_host_connection_remove(hc);
+ }
+ dList_free(host_connections);
+}
/*
* Deallocate memory used by http module
@@ -537,7 +754,9 @@ void a_Http_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*/
void a_Http_freeall(void)
{
+ Http_host_connection_remove_all();
a_Klist_free(&ValidSocks);
a_Url_free(HTTP_Proxy);
dFree(HTTP_Proxy_Auth_base64);
+ dFree(HTTP_Language_hdr);
}
diff --git a/src/IO/iowatch.cc b/src/IO/iowatch.cc
index 0749e24c..f67b711f 100644
--- a/src/IO/iowatch.cc
+++ b/src/IO/iowatch.cc
@@ -22,7 +22,8 @@ using namespace fltk;
//
void a_IOwatch_add_fd(int fd, int when, FileHandler Callback, void *usr_data=0)
{
- add_fd(fd, when, Callback, usr_data);
+ if (fd >= 0)
+ add_fd(fd, when, Callback, usr_data);
}
//
@@ -30,6 +31,7 @@ void a_IOwatch_add_fd(int fd, int when, FileHandler Callback, void *usr_data=0)
//
void a_IOwatch_remove_fd(int fd, int when)
{
- remove_fd(fd, when);
+ if (fd >= 0)
+ remove_fd(fd, when);
}
diff --git a/src/IO/mime.c b/src/IO/mime.c
index 33ec3322..9bffd619 100644
--- a/src/IO/mime.c
+++ b/src/IO/mime.c
@@ -96,16 +96,16 @@ static Viewer_t Mime_major_type_fetch(const char *Key, uint_t Size)
void a_Mime_init()
{
#ifdef ENABLE_GIF
- Mime_add_minor_type("image/gif", a_Gif_image);
+ Mime_add_minor_type("image/gif", a_Dicache_gif_image);
#endif
#ifdef ENABLE_JPEG
- Mime_add_minor_type("image/jpeg", a_Jpeg_image);
- Mime_add_minor_type("image/pjpeg", a_Jpeg_image);
- Mime_add_minor_type("image/jpg", a_Jpeg_image);
+ Mime_add_minor_type("image/jpeg", a_Dicache_jpeg_image);
+ Mime_add_minor_type("image/pjpeg", a_Dicache_jpeg_image);
+ Mime_add_minor_type("image/jpg", a_Dicache_jpeg_image);
#endif
#ifdef ENABLE_PNG
- Mime_add_minor_type("image/png", a_Png_image);
- Mime_add_minor_type("image/x-png", a_Png_image); /* deprecated */
+ Mime_add_minor_type("image/png", a_Dicache_png_image);
+ Mime_add_minor_type("image/x-png", a_Dicache_png_image); /* deprecated */
#endif
Mime_add_minor_type("text/html", a_Html_text);
diff --git a/src/IO/mime.h b/src/IO/mime.h
index 0f51a1e2..0f20cf6d 100644
--- a/src/IO/mime.h
+++ b/src/IO/mime.h
@@ -5,7 +5,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
@@ -13,7 +13,6 @@
#define __MIME_H__
#include <config.h>
-#include <stddef.h>
#ifdef __cplusplus
extern "C" {
@@ -31,18 +30,12 @@ void *a_Html_text (const char *Type,void *web, CA_Callback_t *Call,
void **Data);
void *a_Plain_text(const char *Type,void *web, CA_Callback_t *Call,
void **Data);
-#ifdef ENABLE_JPEG
-void *a_Jpeg_image(const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
-#ifdef ENABLE_PNG
-void *a_Png_image (const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
-#ifdef ENABLE_GIF
-void *a_Gif_image (const char *Type,void *web, CA_Callback_t *Call,
- void **Data);
-#endif
+void *a_Dicache_png_image (const char *Type,void *web, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
/*
* Functions defined inside Mime module
diff --git a/src/Makefile.am b/src/Makefile.am
index d4d03eea..0f09b716 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,25 +1,29 @@
-AM_CPPFLAGS=-DDILLORC_SYS='"$(sysconfdir)/dillorc"' @LIBJPEG_CPPFLAGS@
+AM_CPPFLAGS= \
+ -I$(top_srcdir) \
+ -DDILLO_SYSCONF='"$(sysconfdir)/"' \
+ -DDILLO_DOCDIR='"$(docdir)/"' \
+ @LIBJPEG_CPPFLAGS@
AM_CFLAGS = @LIBPNG_CFLAGS@
-AM_CXXFLAGS = -I.. @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
+AM_CXXFLAGS = @LIBPNG_CFLAGS@ @LIBFLTK_CXXFLAGS@
SUBDIRS = IO
bin_PROGRAMS = dillo
dillo_LDADD = \
- ../dlib/libDlib.a \
- ../dpip/libDpip.a \
+ $(top_builddir)/dlib/libDlib.a \
+ $(top_builddir)/dpip/libDpip.a \
IO/libDiof.a \
- ../dw/libDw-widgets.a \
- ../dw/libDw-fltk.a \
- ../dw/libDw-core.a \
- ../lout/liblout.a \
+ $(top_builddir)/dw/libDw-widgets.a \
+ $(top_builddir)/dw/libDw-fltk.a \
+ $(top_builddir)/dw/libDw-core.a \
+ $(top_builddir)/lout/liblout.a \
@LIBJPEG_LIBS@ @LIBPNG_LIBS@ @LIBFLTK_LIBS@ @LIBZ_LIBS@ @LIBICONV_LIBS@
dillo_SOURCES = \
dillo.cc \
- dir.c \
- dir.h \
+ paths.cc \
+ paths.hh \
ui.cc \
ui.hh \
uicmd.cc \
@@ -28,6 +32,8 @@ dillo_SOURCES = \
bw.c \
cookies.c \
cookies.h \
+ auth.c \
+ auth.h \
colors.c \
colors.h \
binaryconst.h \
@@ -37,6 +43,10 @@ dillo_SOURCES = \
history.c \
prefs.c \
prefs.h \
+ prefsparser.cc \
+ prefsparser.hh \
+ keys.cc \
+ keys.hh \
msg.h \
list.h \
url.c \
@@ -47,6 +57,8 @@ dillo_SOURCES = \
klist.h \
chain.c \
chain.h \
+ utf8.cc \
+ utf8.hh \
timeout.cc \
timeout.hh \
dialog.cc \
@@ -65,6 +77,13 @@ dillo_SOURCES = \
dicache.h \
capi.c \
capi.h \
+ css.cc \
+ css.hh \
+ cssparser.cc \
+ cssparser.hh \
+ doctree.hh \
+ styleengine.cc \
+ styleengine.hh \
plain.cc \
html.cc \
html.hh \
@@ -78,8 +97,13 @@ dillo_SOURCES = \
dns.c \
dns.h \
gif.c \
+ dgif.h \
jpeg.c \
+ djpeg.h \
png.c \
+ dpng.h \
+ imgbuf.cc \
+ imgbuf.hh \
image.cc \
image.hh \
menu.hh \
@@ -88,6 +112,9 @@ dillo_SOURCES = \
dpiapi.h \
pixmaps.h \
findbar.cc \
- findbar.hh
+ findbar.hh \
+ xembed.cc \
+ xembed.hh
-EXTRA_DIST = chg srch
+EXTRA_DIST = chg srch keysrc
+sysconf_DATA = keysrc
diff --git a/src/auth.c b/src/auth.c
new file mode 100644
index 00000000..91baca30
--- /dev/null
+++ b/src/auth.c
@@ -0,0 +1,539 @@
+/*
+ * File: auth.c
+ *
+ * Copyright 2008 Jeremy Henty <onepoint@starurchin.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/* Handling of HTTP AUTH takes place here.
+ * This implementation aims to follow RFC 2617:
+ * http://www.ietf.org/rfc/rfc2617.txt
+ */
+
+
+#include <ctype.h> /* iscntrl */
+#include "auth.h"
+#include "msg.h"
+#include "misc.h"
+#include "dialog.hh"
+#include "../dlib/dlib.h"
+
+
+typedef struct {
+ int ok;
+ const char *realm;
+} AuthParse_t;
+
+typedef struct {
+ char *name;
+ Dlist *paths; /* stripped of any trailing '/', so the root path is "" */
+ char *authorization; /* the authorization request header */
+} AuthRealm_t;
+
+typedef struct {
+ char *scheme;
+ char *authority;
+ Dlist *realms;
+} AuthHost_t;
+
+typedef struct {
+ const char *realm_name;
+ const DilloUrl *url;
+} AuthDialogData_t;
+
+/*
+ * Local data
+ */
+static Dlist *auth_hosts;
+
+/*
+ * Initialize the auth module.
+ */
+void a_Auth_init(void)
+{
+ auth_hosts = dList_new(1);
+}
+
+static AuthParse_t *Auth_parse_new()
+{
+ AuthParse_t *auth_parse = dNew(AuthParse_t, 1);
+ auth_parse->ok = 0;
+ auth_parse->realm = NULL;
+ return auth_parse;
+}
+
+static void Auth_parse_free(AuthParse_t *auth_parse)
+{
+ if (auth_parse) {
+ dFree((void *)auth_parse->realm);
+ dFree(auth_parse);
+ }
+}
+
+static int Auth_path_is_inside(const char *path1, const char *path2, int len)
+{
+ /*
+ * path2 is effectively truncated to length len. Typically len will be
+ * strlen(path2), or 1 less when we want to ignore a trailing '/'.
+ */
+ return
+ strncmp(path1, path2, len) == 0 &&
+ (path1[len] == '\0' || path1[len] == '/');
+}
+
+/*
+ * Check valid chars.
+ * Return: 0 if invalid, 1 otherwise.
+ */
+static int Auth_is_token_char(char c)
+{
+ const char *invalid = "\"()<>@,;:\\[]?=/{} \t";
+ return (strchr(invalid, c) || iscntrl((uchar_t)c)) ? 0 : 1;
+}
+
+/*
+ * Unquote the content of a quoted string.
+ * Return: newly allocated unquoted content.
+ *
+ * Arguments:
+ * quoted: pointer to the first char *after* the initial double quote.
+ * size: the number of chars in the result, *not* including a final '\0'.
+ *
+ * Preconditions:
+ * quoted points to a correctly quoted and escaped string.
+ * size is the number of characters in the quoted string, *after*
+ * removing escape characters.
+ *
+ */
+static const char *Auth_unquote_value(const char *quoted, int size)
+{
+ char c, *value, *value_ptr;
+ value_ptr = value = dNew(char, size + 1);
+ while ((c = *quoted++) != '"')
+ *value_ptr++ = (c == '\\') ? *quoted++ : c;
+ *value_ptr = '\0';
+ return value;
+}
+
+/*
+ * Parse a quoted string. Save the result as the auth realm if required.
+ * Return: 1 if the parse succeeds, 0 otherwise.
+ */
+static int Auth_parse_quoted_string(AuthParse_t *auth_parse, int set_realm,
+ char **auth)
+{
+ char *value;
+ int size;
+
+ /* parse the '"' */
+ switch (*(*auth)++) {
+ case '"':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: missing Basic auth token value after '='\n");
+ return 0;
+ break;
+ default:
+ MSG("auth.c: garbage in Basic auth after '='\n");
+ return 0;
+ break;
+ }
+
+ /* parse the rest */
+ value = *auth;
+ size = 0;
+ while (1) {
+ switch (*(*auth)++) {
+ case '"':
+ if (set_realm) {
+ dFree((void *)auth_parse->realm);
+ auth_parse->realm = Auth_unquote_value(value, size);
+ auth_parse->ok = 1;
+ }
+ return 1;
+ break;
+ case '\0':
+ MSG("auth.c: auth string ended inside quoted string value\n");
+ return 0;
+ break;
+ case '\\':
+ /* end of string? */
+ if (!*(*auth)++) {
+ MSG("auth.c: "
+ "auth string ended inside quoted string value "
+ "immediately after \\\n");
+ return 0;
+ }
+ /* fall through to the next case */
+ default:
+ size++;
+ break;
+ }
+ }
+}
+
+/*
+ * Parse a token-value pair.
+ * Return: 1 if the parse succeeds, 0 otherwise.
+ */
+static int Auth_parse_token_value(AuthParse_t *auth_parse, char **auth)
+{
+ char *token;
+ int token_size, set_realm;
+ static const char realm_token[] = "realm";
+
+ /* parse a token */
+ token = *auth;
+ token_size = 0;
+ while (Auth_is_token_char(**auth)) {
+ (*auth)++;
+ token_size++;
+ }
+ if (token_size == 0) {
+ MSG("auth.c: Auth_parse_token_value: "
+ "missing Basic auth token\n");
+ return 0;
+ }
+
+ /* skip space characters */
+ while (**auth == ' ')
+ (*auth)++;
+
+ /* parse the '=' */
+ switch (*(*auth)++) {
+ case '=':
+ break;
+ case '\0':
+ case ',':
+ MSG("auth.c: Auth_parse_token_value: "
+ "missing Basic auth token value\n");
+ return 0;
+ break;
+ default:
+ MSG("auth.c: Auth_parse_token_value: "
+ "garbage after Basic auth token\n");
+ return 0;
+ break;
+ }
+
+ /* skip space characters */
+ while (**auth == ' ')
+ (*auth)++;
+
+ /* is this value the realm? */
+ set_realm =
+ auth_parse->realm == NULL &&
+ dStrncasecmp(realm_token,token,token_size) == 0 &&
+ strlen(realm_token) == (size_t)token_size;
+
+ return Auth_parse_quoted_string(auth_parse, set_realm, auth);
+}
+
+static void Auth_parse_auth_basic(AuthParse_t *auth_parse, char **auth)
+{
+ int token_value_pairs_found;
+
+ /* parse comma-separated token-value pairs */
+ token_value_pairs_found = 0;
+ while (1) {
+ /* skip space and comma characters */
+ while (**auth == ' ' || **auth == ',')
+ (*auth)++;
+ /* end of string? */
+ if (!**auth)
+ break;
+ /* parse token-value pair */
+ if (!Auth_parse_token_value(auth_parse, auth))
+ break;
+ token_value_pairs_found = 1;
+ }
+
+ if (!token_value_pairs_found) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth token-value pairs\n");
+ return;
+ }
+
+ if (!auth_parse->realm) {
+ MSG("auth.c: Auth_parse_auth_basic: "
+ "missing Basic auth realm\n");
+ return;
+ }
+}
+
+static void Auth_parse_auth(AuthParse_t *auth_parse, char *auth)
+{
+ _MSG("auth.c: Auth_parse_auth: auth = '%s'\n", auth);
+ if (dStrncasecmp(auth, "Basic ", 6) == 0) {
+ auth += 6;
+ Auth_parse_auth_basic(auth_parse, &auth);
+ } else {
+ MSG("auth.c: Auth_parse_auth: "
+ "unknown authorization scheme: auth = {%s}\n",
+ auth);
+ }
+}
+
+/*
+ * Return the host that contains a URL, or NULL if there is no such host.
+ */
+static AuthHost_t *Auth_host_by_url(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ int i;
+
+ for (i = 0; (host = dList_nth_data(auth_hosts, i)); i++)
+ if (((dStrcasecmp(URL_SCHEME(url), host->scheme) == 0) &&
+ (dStrcasecmp(URL_AUTHORITY(url), host->authority) == 0)))
+ return host;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the given name.
+ */
+static AuthRealm_t *Auth_realm_by_name(const AuthHost_t *host,
+ const char *name)
+{
+ AuthRealm_t *realm;
+ int i;
+
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++)
+ if (strcmp(realm->name,name) == 0)
+ return realm;
+
+ return NULL;
+}
+
+/*
+ * Search all realms for the one with the best-matching path.
+ */
+static AuthRealm_t *Auth_realm_by_path(const AuthHost_t *host,
+ const char *path)
+{
+ AuthRealm_t *realm_best, *realm;
+ int i, j;
+ int match_length;
+
+ match_length = 0;
+ realm_best = NULL;
+ for (i = 0; (realm = dList_nth_data(host->realms, i)); i++) {
+ char *realm_path;
+
+ for (j = 0; (realm_path = dList_nth_data(realm->paths, j)); j++) {
+ int realm_path_length = strlen(realm_path);
+ if (Auth_path_is_inside(path, realm_path, realm_path_length) &&
+ !(realm_best && match_length >= realm_path_length)) {
+ realm_best = realm;
+ match_length = realm_path_length;
+ }
+ }
+ }
+
+ return realm_best;
+}
+
+static int Auth_realm_includes_path(const AuthRealm_t *realm, const char *path)
+{
+ int i;
+ char *realm_path;
+
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++)
+ if (Auth_path_is_inside(path, realm_path, strlen(realm_path)))
+ return 1;
+
+ return 0;
+}
+
+static void Auth_realm_add_path(AuthRealm_t *realm, const char *path)
+{
+ int len, i;
+ char *realm_path, *n_path;
+
+ n_path = dStrdup(path);
+ len = strlen(n_path);
+
+ /* remove trailing '/' */
+ if (len && n_path[len - 1] == '/')
+ n_path[--len] = 0;
+
+ /* delete existing paths that are inside the new one */
+ for (i = 0; (realm_path = dList_nth_data(realm->paths, i)); i++) {
+ if (Auth_path_is_inside(realm_path, path, len)) {
+ dList_remove_fast(realm->paths, realm_path);
+ dFree(realm_path);
+ i--; /* reconsider this slot */
+ }
+ }
+
+ dList_append(realm->paths, n_path);
+}
+
+/*
+ * Return the authorization header for an HTTP query.
+ */
+const char *a_Auth_get_auth_str(const DilloUrl *url)
+{
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ return
+ ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_path(host, URL_PATH(url)))) ?
+ realm->authorization : NULL;
+}
+
+/*
+ * Determine whether the user needs to authenticate.
+ */
+static int Auth_do_auth_required(const char *realm_name, const DilloUrl *url)
+{
+ /*
+ * TO DO: I dislike the way that this code must decide whether we
+ * sent authentication during the request and trust us to resend it
+ * after the reload. Could it be more robust if every DilloUrl
+ * recorded its authentication, and whether it was accepted? (JCH)
+ */
+
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+
+ /*
+ * The size of the following comments reflects the concerns in the
+ * TO DO at the top of this function. It should not be so hard to
+ * explain why code is correct! (JCH)
+ */
+
+ /*
+ * If we have authentication but did not send it (because we did
+ * not know this path was in the realm) then we update the realm.
+ * We do not re-authenticate because our authentication is probably
+ * OK. Thanks to the updated realm the forthcoming reload will
+ * make us send the authentication. If our authentication is not
+ * OK the server will challenge us again after the reload and then
+ * we will re-authenticate.
+ */
+ if ((host = Auth_host_by_url(url)) &&
+ (realm = Auth_realm_by_name(host, realm_name)) &&
+ (!Auth_realm_includes_path(realm, URL_PATH(url)))) {
+ _MSG("Auth_do_auth_required: updating realm '%s' with URL '%s'\n",
+ realm_name, URL_STR(url));
+ Auth_realm_add_path(realm, URL_PATH(url));
+ return 0;
+ }
+
+ /*
+ * Either we had no authentication or we sent it and the server
+ * rejected it, so we must re-authenticate.
+ */
+ return 1;
+}
+
+static void Auth_do_auth_dialog_cb(const char *user, const char *password,
+ void *vData)
+{
+ AuthDialogData_t *data;
+ AuthHost_t *host;
+ AuthRealm_t *realm;
+ char *user_password, *response, *authorization, *authorization_old;
+
+ data = (AuthDialogData_t *)vData;
+
+ /* find or create the host */
+ if (!(host = Auth_host_by_url(data->url))) {
+ /* create a new host */
+ host = dNew(AuthHost_t, 1);
+ host->scheme = dStrdup(URL_SCHEME(data->url));
+ host->authority = dStrdup(URL_AUTHORITY(data->url));
+ host->realms = dList_new(1);
+ dList_append(auth_hosts, host);
+ }
+
+ /* find or create the realm */
+ if (!(realm = Auth_realm_by_name(host, data->realm_name))) {
+ /* create a new realm */
+ realm = dNew(AuthRealm_t, 1);
+ realm->name = dStrdup(data->realm_name);
+ realm->paths = dList_new(1);
+ realm->authorization = NULL;
+ dList_append(host->realms, realm);
+ }
+
+ Auth_realm_add_path(realm, URL_PATH(data->url));
+
+ /* create and set the authorization */
+ user_password = dStrconcat(user, ":", password, NULL);
+ response = a_Misc_encode_base64(user_password);
+ authorization =
+ dStrconcat("Authorization: Basic ", response, "\r\n", NULL);
+ authorization_old = realm->authorization;
+ realm->authorization = authorization;
+ dFree(authorization_old);
+ dFree(user_password);
+ dFree(response);
+}
+
+static int Auth_do_auth_dialog(const char *realm, const DilloUrl *url)
+{
+ int ret;
+ char *message;
+ AuthDialogData_t *data;
+
+ _MSG("auth.c: Auth_do_auth_dialog: realm = '%s'\n", realm);
+ message = dStrconcat("Enter a user and password for \"",
+ realm, "\".", NULL);
+ data = dNew(AuthDialogData_t, 1);
+ data->realm_name = dStrdup(realm);
+ data->url = a_Url_dup(url);
+ ret = a_Dialog_user_password(message, Auth_do_auth_dialog_cb, data);
+ dFree(message);
+ dFree((void*)data->realm_name);
+ a_Url_free((void*)data->url);
+ dFree(data);
+ return ret;
+}
+
+/*
+ * Do authorization for an auth string.
+ */
+static int Auth_do_auth(char *auth, const DilloUrl *url)
+{
+ int reload;
+ AuthParse_t *auth_parse;
+
+ _MSG("auth.c: Auth_do_auth: auth={%s}\n", auth);
+ reload = 0;
+ auth_parse = Auth_parse_new();
+ Auth_parse_auth(auth_parse, auth);
+ if (auth_parse->ok)
+ reload =
+ Auth_do_auth_required(auth_parse->realm, url) ?
+ Auth_do_auth_dialog(auth_parse->realm, url)
+ : 1;
+ Auth_parse_free(auth_parse);
+
+ return reload;
+}
+
+/*
+ * Do authorization for a set of auth strings.
+ */
+int a_Auth_do_auth(Dlist *auths, const DilloUrl *url)
+{
+ int reload, i;
+ char *auth;
+
+ reload = 0;
+ for (i = 0; (auth = dList_nth_data(auths, i)); ++i)
+ if (Auth_do_auth(auth, url))
+ reload = 1;
+
+ return reload;
+}
+
diff --git a/src/auth.h b/src/auth.h
new file mode 100644
index 00000000..5f96f642
--- /dev/null
+++ b/src/auth.h
@@ -0,0 +1,19 @@
+#ifndef __AUTH_H__
+#define __AUTH_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+
+
+const char *a_Auth_get_auth_str(const DilloUrl *request_url);
+int a_Auth_do_auth(Dlist *auth_string, const DilloUrl *url);
+void a_Auth_init(void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__AUTH_H__ */
diff --git a/src/bookmark.c b/src/bookmark.c
index 588e51f8..9a594789 100644
--- a/src/bookmark.c
+++ b/src/bookmark.c
@@ -9,10 +9,7 @@
* (at your option) any later version.
*/
-#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include "msg.h"
#include "history.h"
diff --git a/src/bw.c b/src/bw.c
index 27f3066c..7f4785bb 100644
--- a/src/bw.c
+++ b/src/bw.c
@@ -13,6 +13,7 @@
#include "bw.h"
+#include "msg.h"
#include "list.h"
#include "capi.h"
#include "uicmd.hh"
@@ -58,11 +59,14 @@ BrowserWindow *a_Bw_new()
bw->nav_expect_url = NULL;
bw->redirect_level = 0;
+ bw->meta_refresh_status = 0;
+ bw->meta_refresh_url = NULL;
bw->RootClients = dList_new(8);
bw->ImageClients = dList_new(8);
bw->NumImages = 0;
bw->NumImagesGot = 0;
+ bw->NumPendingStyleSheets = 0;
bw->PageUrls = dList_new(8);
bw->Docs = dList_new(8);
@@ -99,6 +103,8 @@ void a_Bw_free(BrowserWindow *bw)
dFree(dList_nth_data(bw->nav_stack, j));
dList_free(bw->nav_stack);
+ a_Url_free(bw->meta_refresh_url);
+
dStr_free(bw->page_bugs, 1);
dFree(bw);
break;
@@ -176,7 +182,7 @@ void a_Bw_stop_clients(BrowserWindow *bw, int flags)
if (flags & BW_Root) {
/* Remove root clients */
while ((data = dList_nth_data(bw->RootClients, 0))) {
- a_Capi_stop_client(VOIDP2INT(data), (flags & Bw_Force));
+ a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
dList_remove_fast(bw->RootClients, data);
}
}
@@ -184,7 +190,7 @@ void a_Bw_stop_clients(BrowserWindow *bw, int flags)
if (flags & BW_Img) {
/* Remove image clients */
while ((data = dList_nth_data(bw->ImageClients, 0))) {
- a_Capi_stop_client(VOIDP2INT(data), (flags & Bw_Force));
+ a_Capi_stop_client(VOIDP2INT(data), (flags & BW_Force));
dList_remove_fast(bw->ImageClients, data);
}
}
@@ -216,6 +222,38 @@ void a_Bw_add_doc(BrowserWindow *bw, void *vdoc)
}
/*
+ * Get current document.
+ */
+void *a_Bw_get_current_doc(BrowserWindow *bw)
+{
+ void *doc = NULL;
+ int len = dList_length(bw->Docs);
+
+ if (len == 1)
+ doc = dList_nth_data(bw->Docs, 0);
+ else if (len > 1)
+ MSG("a_Bw_get_current_doc() multiple docs not implemented\n");
+
+ return doc;
+}
+
+/*
+ * Get document by URL.
+ *
+ * This is currently used by popup menus that need to ensure that the
+ * page has not changed while the menu was popped up.
+ */
+void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *url)
+{
+ void *doc = NULL;
+
+ if (url && dList_find_custom(bw->PageUrls, url, (dCompareFunc)a_Url_cmp)) {
+ doc = a_Bw_get_current_doc(bw);
+ }
+ return doc;
+}
+
+/*
* Remove a document from the bw's list
*/
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc)
@@ -253,6 +291,9 @@ void a_Bw_cleanup(BrowserWindow *bw)
/* Zero image-progress data */
bw->NumImages = 0;
bw->NumImagesGot = 0;
+
+ /* Zero stylesheet counter */
+ bw->NumPendingStyleSheets = 0;
}
/*--------------------------------------------------------------------------*/
diff --git a/src/bw.h b/src/bw.h
index f0d8dd7d..4b915b5c 100644
--- a/src/bw.h
+++ b/src/bw.h
@@ -1,8 +1,6 @@
#ifndef __BW_H__
#define __BW_H__
-#include <sys/types.h>
-
#include "url.h" /* for DilloUrl */
/*
@@ -10,7 +8,7 @@
*/
#define BW_Root (1) /* Root URLs */
#define BW_Img (2) /* Image URLs */
-#define Bw_Force (4) /* Stop connection too */
+#define BW_Force (4) /* Stop connection too */
typedef struct _BrowserWindow BrowserWindow;
@@ -38,6 +36,8 @@ struct _BrowserWindow
int NumImages;
/* Number of images already loaded */
int NumImagesGot;
+ /* Number of not yet arrived style sheets */
+ int NumPendingStyleSheets;
/* List of all Urls requested by this page (and its types) */
Dlist *PageUrls;
@@ -57,8 +57,11 @@ struct _BrowserWindow
* redirection loops (accounts for WEB_RootUrl only) */
int redirect_level;
- /* TODO: maybe this fits better in the linkblock.
- * Although having it here avoids having a signal for handling it. */
+ /* Url for zero-delay redirections in the META element */
+ int meta_refresh_status;
+ DilloUrl *meta_refresh_url;
+
+ /* HTML-bugs detected at parse time */
int num_page_bugs;
Dstr *page_bugs;
};
@@ -80,10 +83,13 @@ int a_Bw_remove_client(BrowserWindow *bw, int ClientKey);
void a_Bw_close_client(BrowserWindow *bw, int ClientKey);
void a_Bw_stop_clients(BrowserWindow *bw, int flags);
void a_Bw_add_doc(BrowserWindow *bw, void *vdoc);
+void *a_Bw_get_current_doc(BrowserWindow *bw);
+void *a_Bw_get_url_doc(BrowserWindow *bw, const DilloUrl *Url);
void a_Bw_remove_doc(BrowserWindow *bw, void *vdoc);
void a_Bw_add_url(BrowserWindow *bw, const DilloUrl *Url);
void a_Bw_cleanup(BrowserWindow *bw);
+typedef void (*BwCallback_t)(BrowserWindow *bw, const void *data);
#ifdef __cplusplus
}
diff --git a/src/cache.c b/src/cache.c
index 9ddb0abf..fcd27a05 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -16,11 +16,8 @@
#include <ctype.h> /* for tolower */
#include <sys/types.h>
-#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
#include "msg.h"
#include "IO/Url.h"
@@ -32,6 +29,7 @@
#include "misc.h"
#include "capi.h"
#include "decode.h"
+#include "auth.h"
#include "timeout.hh"
#include "uicmd.hh"
@@ -52,8 +50,10 @@ typedef struct {
char *TypeDet; /* MIME type string (detected from data) */
char *TypeHdr; /* MIME type string as from the HTTP Header */
char *TypeMeta; /* MIME type string from META HTTP-EQUIV */
+ char *TypeNorm; /* MIME type string normalized */
Dstr *Header; /* HTTP header */
const DilloUrl *Location; /* New URI for redirects */
+ Dlist *Auth; /* Authentication fields */
Dstr *Data; /* Pointer to raw data */
Dstr *UTF8Data; /* Data after charset translation */
int DataRefcount; /* Reference count */
@@ -85,9 +85,10 @@ static uint_t DelayedQueueIdleId = 0;
/*
* Forward declarations
*/
-static void Cache_process_queue(CacheEntry_t *entry);
+static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry);
static void Cache_delayed_process_queue(CacheEntry_t *entry);
-
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw);
+static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds);
/*
* Determine if two cache entries are equal (used by CachedURLs)
@@ -123,7 +124,7 @@ void a_Cache_init(void)
{
DilloUrl *url = a_Url_new("about:splash", NULL);
Dstr *ds = dStr_new(AboutSplash);
- a_Cache_entry_inject(url, ds);
+ Cache_entry_inject(url, ds);
dStr_free(ds, 1);
a_Url_free(url);
}
@@ -132,18 +133,6 @@ void a_Cache_init(void)
/* Client operations ------------------------------------------------------ */
/*
- * Make a unique primary-key for cache clients
- */
-static int Cache_client_make_key(void)
-{
- static int ClientKey = 0; /* Provide a primary key for each client */
-
- if (++ClientKey < 0)
- ClientKey = 1;
- return ClientKey;
-}
-
-/*
* Add a client to ClientQueue.
* - Every client-field is just a reference (except 'Web').
* - Return a unique number for identifying the client.
@@ -151,14 +140,18 @@ static int Cache_client_make_key(void)
static int Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web,
CA_Callback_t Callback, void *CbData)
{
- int ClientKey;
+ static int ClientKey = 0; /* Provide a primary key for each client */
CacheClient_t *NewClient;
+ if (++ClientKey <= 0)
+ ClientKey = 1;
+
NewClient = dNew(CacheClient_t, 1);
- ClientKey = Cache_client_make_key();
NewClient->Key = ClientKey;
NewClient->Url = Url;
+ NewClient->Version = 0;
NewClient->Buf = NULL;
+ NewClient->BufSize = 0;
NewClient->Callback = Callback;
NewClient->CbData = CbData;
NewClient->Web = Web;
@@ -204,8 +197,10 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
NewEntry->TypeDet = NULL;
NewEntry->TypeHdr = NULL;
NewEntry->TypeMeta = NULL;
+ NewEntry->TypeNorm = NULL;
NewEntry->Header = dStr_new("");
NewEntry->Location = NULL;
+ NewEntry->Auth = NULL;
NewEntry->Data = dStr_sized_new(8*1024);
NewEntry->UTF8Data = NULL;
NewEntry->DataRefcount = 0;
@@ -272,7 +267,7 @@ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
* Inject full page content directly into the cache.
* Used for "about:splash". May be used for "about:cache" too.
*/
-void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
+static void Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
{
CacheEntry_t *entry;
@@ -288,6 +283,18 @@ void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds)
}
/*
+ * Free Authentication fields.
+ */
+static void Cache_auth_free(Dlist *auth)
+{
+ int i;
+ void *auth_field;
+ for (i = 0; (auth_field = dList_nth_data(auth, i)); ++i)
+ dFree(auth_field);
+ dList_free(auth);
+}
+
+/*
* Free the components of a CacheEntry_t struct.
*/
static void Cache_entry_free(CacheEntry_t *entry)
@@ -296,8 +303,10 @@ static void Cache_entry_free(CacheEntry_t *entry)
dFree(entry->TypeDet);
dFree(entry->TypeHdr);
dFree(entry->TypeMeta);
+ dFree(entry->TypeNorm);
dStr_free(entry->Header, TRUE);
a_Url_free((DilloUrl *)entry->Location);
+ Cache_auth_free(entry->Auth);
dStr_free(entry->Data, 1);
dStr_free(entry->UTF8Data, 1);
if (entry->CharsetDecoder)
@@ -353,14 +362,13 @@ void a_Cache_entry_remove_by_url(DilloUrl *url)
* Try finding the url in the cache. If it hits, send the cache contents
* from there. If it misses, set up a new connection.
*
- * - 'Web' is an auxiliar data structure with misc. parameters.
+ * - 'Web' is an auxiliary data structure with misc. parameters.
* - 'Call' is the callback that receives the data
* - 'CbData' is custom data passed to 'Call'
* Note: 'Call' and/or 'CbData' can be NULL, in that case they get set
* later by a_Web_dispatch_by_type, based on content/type and 'Web' data.
*
* Return value: A primary key for identifying the client,
- * 0 if the client is aborted in the process.
*/
int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
{
@@ -394,6 +402,15 @@ int a_Cache_open_url(void *web, CA_Callback_t Call, void *CbData)
*/
uint_t a_Cache_get_flags(const DilloUrl *url)
{
+ CacheEntry_t *entry = Cache_entry_search(url);
+ return (entry ? entry->Flags : 0);
+}
+
+/*
+ * Get cache entry status (following redirections).
+ */
+uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url)
+{
CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
return (entry ? entry->Flags : 0);
}
@@ -406,7 +423,9 @@ static void Cache_ref_data(CacheEntry_t *entry)
if (entry) {
entry->DataRefcount++;
_MSG("DataRefcount++: %d\n", entry->DataRefcount);
- if (entry->CharsetDecoder && entry->DataRefcount == 1) {
+ if (entry->CharsetDecoder &&
+ (!entry->UTF8Data || entry->DataRefcount == 1)) {
+ dStr_free(entry->UTF8Data, 1);
entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
entry->Data->str,
entry->Data->len);
@@ -440,8 +459,8 @@ static void Cache_unref_data(CacheEntry_t *entry)
*/
static const char *Cache_current_content_type(CacheEntry_t *entry)
{
- return entry->TypeMeta ? entry->TypeMeta : entry->TypeHdr ? entry->TypeHdr :
- entry->TypeDet;
+ return entry->TypeNorm ? entry->TypeNorm : entry->TypeMeta ? entry->TypeMeta
+ : entry->TypeHdr ? entry->TypeHdr : entry->TypeDet;
}
/*
@@ -464,44 +483,53 @@ static Dstr *Cache_data(CacheEntry_t *entry)
/*
* Change Content-Type for cache entry found by url.
+ * from = { "http" | "meta" }
* Return new content type.
*/
const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force)
+ const char *from)
{
const char *curr;
- CacheEntry_t *entry = Cache_entry_search_with_redirect(url);
+ char *major, *minor, *charset;
+ CacheEntry_t *entry = Cache_entry_search(url);
- if (!entry)
- return NULL;
+ dReturn_val_if_fail (entry != NULL, NULL);
- curr = Cache_current_content_type(entry);
- if (entry->TypeMeta && (force == FALSE)) {
- /* it's already been set */
- return curr;
- }
-
- if (a_Misc_content_type_cmp(curr, ctype)) {
- char *charset;
+ _MSG("a_Cache_set_content_type {%s} {%s}\n", ctype, URL_STR(url));
- dFree(entry->TypeMeta);
- curr = entry->TypeMeta = dStrdup(ctype);
-
- if (entry->CharsetDecoder)
- a_Decode_free(entry->CharsetDecoder);
- a_Misc_parse_content_type(ctype, NULL, NULL, &charset);
- entry->CharsetDecoder = a_Decode_charset_init(charset);
- dFree(charset);
+ curr = Cache_current_content_type(entry);
+ if (entry->TypeMeta || (*from == 'h' && entry->TypeHdr) ) {
+ /* Type is already been set. Do nothing.
+ * BTW, META overrides TypeHdr */
+ } else {
+ if (*from == 'h') {
+ /* Content-Type from HTTP header */
+ entry->TypeHdr = dStrdup(ctype);
+ } else {
+ /* Content-Type from META */
+ entry->TypeMeta = dStrdup(ctype);
+ }
+ if (a_Misc_content_type_cmp(curr, ctype)) {
+ /* ctype gives one different from current */
+ a_Misc_parse_content_type(ctype, &major, &minor, &charset);
+ if (*from == 'm' && charset &&
+ ((!major || !*major) && (!minor || !*minor))) {
+ /* META only gives charset; use detected MIME type too */
+ entry->TypeNorm = dStrconcat(entry->TypeDet, ctype, NULL);
+ }
+ if (charset) {
+ if (entry->CharsetDecoder)
+ a_Decode_free(entry->CharsetDecoder);
+ entry->CharsetDecoder = a_Decode_charset_init(charset);
+ curr = Cache_current_content_type(entry);
- dStr_free(entry->UTF8Data, 1);
- if (entry->CharsetDecoder && entry->DataRefcount > 0)
- entry->UTF8Data = a_Decode_process(entry->CharsetDecoder,
- entry->Data->str,
- entry->Data->len);
- else
- entry->UTF8Data = NULL;
+ /* Invalidate UTF8Data */
+ dStr_free(entry->UTF8Data, 1);
+ entry->UTF8Data = NULL;
+ }
+ dFree(major); dFree(minor); dFree(charset);
+ }
}
-
return curr;
}
@@ -513,8 +541,9 @@ int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
{
CacheEntry_t *entry = Cache_entry_search_with_redirect(Url);
if (entry) {
+ Dstr *data;
Cache_ref_data(entry);
- Dstr *data = Cache_data(entry);
+ data = Cache_data(entry);
*PBuf = data->str;
*BufSize = data->len;
} else {
@@ -556,14 +585,16 @@ static char *Cache_parse_field(const char *header, const char *fieldname)
}
i += j;
- while (header[i] == ' ') i++;
if (header[i] == ':') {
/* Field found! */
while (header[++i] == ' ' || header[i] == '\t');
for (j = 0; header[i + j] != '\n'; j++);
+ while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
+ j--;
field = dStrndup(header + i, j);
return field;
}
+ while (header[i] != '\n') i++;
}
return NULL;
}
@@ -590,13 +621,16 @@ static Dlist *Cache_parse_multiple_fields(const char *header,
}
i += j;
- for ( ; header[i] == ' '; i++);
if (header[i] == ':') {
/* Field found! */
while (header[++i] == ' ' || header[i] == '\t');
for (j = 0; header[i + j] != '\n'; j++);
+ while (j && (header[i + j - 1] == ' ' || header[i + j - 1] == '\t'))
+ j--;
field = dStrndup(header + i, j);
dList_append(fields, field);
+ } else {
+ while (header[i] != '\n') i++;
}
}
@@ -614,15 +648,16 @@ static Dlist *Cache_parse_multiple_fields(const char *header,
static void Cache_parse_header(CacheEntry_t *entry)
{
char *header = entry->Header->str;
- char *Length, *Type, *location_str, *encoding, *charset;
+ char *Length, *Type, *location_str, *encoding;
#ifndef DISABLE_COOKIES
Dlist *Cookies;
#endif
- DilloUrl *location_url;
Dlist *warnings;
void *data;
int i;
+ _MSG("Cache_parse_header\n");
+
if (entry->Header->len > 12) {
if (header[9] == '1' && header[10] == '0' && header[11] == '0') {
/* 100: Continue. The "real" header has not come yet. */
@@ -634,26 +669,31 @@ static void Cache_parse_header(CacheEntry_t *entry)
}
if (header[9] == '3' && header[10] == '0') {
/* 30x: URL redirection */
- entry->Flags |= CA_Redirect;
- if (header[11] == '1')
- entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
- else if (header[11] == '2')
- entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
-
- location_str = Cache_parse_field(header, "Location");
- location_url = a_Url_new(location_str, URL_STR_(entry->Url));
- if (URL_FLAGS(location_url) & (URL_Post + URL_Get) &&
- dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 &&
- dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) {
- /* Forbid dpi GET and POST from non dpi-generated urls */
- MSG("Redirection Denied! '%s' -> '%s'\n",
- URL_STR(entry->Url), URL_STR(location_url));
- a_Url_free(location_url);
- } else {
- entry->Location = location_url;
+ if ((location_str = Cache_parse_field(header, "Location"))) {
+ DilloUrl *location_url;
+
+ entry->Flags |= CA_Redirect;
+ if (header[11] == '1')
+ entry->Flags |= CA_ForceRedirect; /* 301 Moved Permanently */
+ else if (header[11] == '2')
+ entry->Flags |= CA_TempRedirect; /* 302 Temporary Redirect */
+
+ location_url = a_Url_new(location_str, URL_STR_(entry->Url));
+ if (URL_FLAGS(location_url) & (URL_Post + URL_Get) &&
+ dStrcasecmp(URL_SCHEME(location_url), "dpi") == 0 &&
+ dStrcasecmp(URL_SCHEME(entry->Url), "dpi") != 0) {
+ /* Forbid dpi GET and POST from non dpi-generated urls */
+ MSG("Redirection Denied! '%s' -> '%s'\n",
+ URL_STR(entry->Url), URL_STR(location_url));
+ a_Url_free(location_url);
+ } else {
+ entry->Location = location_url;
+ }
+ dFree(location_str);
}
- dFree(location_str);
-
+ } else if (strncmp(header + 9, "401", 3) == 0) {
+ entry->Auth =
+ Cache_parse_multiple_fields(header, "WWW-Authenticate");
} else if (strncmp(header + 9, "404", 3) == 0) {
entry->Flags |= CA_NotFound;
}
@@ -667,16 +707,22 @@ static void Cache_parse_header(CacheEntry_t *entry)
dList_free(warnings);
}
+ /*
+ * Get Transfer-Encoding and initialize decoder
+ */
+ encoding = Cache_parse_field(header, "Transfer-Encoding");
+ entry->TransferDecoder = a_Decode_transfer_init(encoding);
+
+
if ((Length = Cache_parse_field(header, "Content-Length")) != NULL) {
- char *tmp;
- if ((tmp = Cache_parse_field(header, "Transfer-Encoding"))) {
+ if (encoding) {
/*
- * BUG: Should test for _presence_ of headers, not whether they
- * have content.
+ * If Transfer-Encoding is present, Content-Length must be ignored.
+ * If the Transfer-Encoding is non-identity, it is an error.
*/
- MSG_HTTP("Both Content-Length and Transfer-Encoding headers"
- " received.\n");
- dFree(tmp);
+ if (dStrcasecmp(encoding, "identity"))
+ MSG_HTTP("Content-Length and non-identity Transfer-Encoding "
+ "headers both present.\n");
} else {
entry->Flags |= CA_GotLength;
entry->ExpectedSize = MAX(strtol(Length, NULL, 10), 0);
@@ -684,27 +730,21 @@ static void Cache_parse_header(CacheEntry_t *entry)
dFree(Length);
}
+ dFree(encoding); /* free Transfer-Encoding */
+
#ifndef DISABLE_COOKIES
- /* BUG: If a server feels like mixing Set-Cookie2 and Set-Cookie
- * responses which aren't identical, then we have a problem. I don't
- * know if that is a real issue though. */
- if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie2")) ||
- (Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
- a_Cookies_set(Cookies, entry->Url);
+ if ((Cookies = Cache_parse_multiple_fields(header, "Set-Cookie"))) {
+ char *server_date = Cache_parse_field(header, "Date");
+
+ a_Cookies_set(Cookies, entry->Url, server_date);
for (i = 0; (data = dList_nth_data(Cookies, i)); ++i)
dFree(data);
dList_free(Cookies);
+ dFree(server_date);
}
#endif /* !DISABLE_COOKIES */
/*
- * Get Transfer-Encoding and initialize decoder
- */
- encoding = Cache_parse_field(header, "Transfer-Encoding");
- entry->TransferDecoder = a_Decode_transfer_init(encoding);
- dFree(encoding);
-
- /*
* Get Content-Encoding and initialize decoder
*/
encoding = Cache_parse_field(header, "Content-Encoding");
@@ -721,28 +761,17 @@ static void Cache_parse_header(CacheEntry_t *entry)
dStr_free(entry->Data, 1);
entry->Data = dStr_sized_new(MIN(entry->ExpectedSize, MAX_INIT_BUF));
}
- Cache_ref_data(entry);
/* Get Content-Type */
- if ((Type = Cache_parse_field(header, "Content-Type")) == NULL) {
- if (!((entry->Flags & CA_GotLength) && (entry->ExpectedSize == 0))) {
- /* unless the server sent Content-Length: 0 */
- MSG_HTTP("Server didn't send Content-Type in header.\n");
- }
- } else {
- entry->TypeHdr = Type;
- _MSG("Content-Type {%s} {%s}\n", Type, URL_STR(entry->Url));
- /* This Content-Type is not trusted. It's checked against real data
- * in Cache_process_queue(); only then CA_GotContentType becomes true.
- */
- a_Misc_parse_content_type(Type, NULL, NULL, &charset);
- if (charset) {
- entry->CharsetDecoder = a_Decode_charset_init(charset);
- if (entry->CharsetDecoder)
- entry->UTF8Data = dStr_new("");
- dFree(charset);
- }
+ if ((Type = Cache_parse_field(header, "Content-Type"))) {
+ /* This HTTP Content-Type is not trusted. It's checked against real data
+ * in Cache_process_queue(); only then CA_GotContentType becomes true. */
+ a_Cache_set_content_type(entry->Url, Type, "http");
+ _MSG("TypeHdr {%s} {%s}\n", Type, URL_STR(entry->Url));
+ _MSG("TypeMeta {%s}\n", entry->TypeMeta);
+ dFree(Type);
}
+ Cache_ref_data(entry);
}
/*
@@ -762,7 +791,7 @@ static int Cache_get_header(CacheEntry_t *entry,
continue;
if (N == 1 && (buf[i] == ' ' || buf[i] == '\t')) {
/* unfold multiple-line header */
- MSG("Multiple-line header!\n");
+ _MSG("Multiple-line header!\n");
dStr_erase(hdr, hdr->len - 1, 1);
}
N = (buf[i] == '\n') ? N + 1 : 0;
@@ -791,15 +820,64 @@ static int Cache_get_header(CacheEntry_t *entry,
void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
const DilloUrl *Url)
{
- int offset = 0;
- int len;
+ int offset, len;
const char *str;
+ Dstr *dstr1, *dstr2, *dstr3;
CacheEntry_t *entry = Cache_entry_search(Url);
/* Assert a valid entry (not aborted) */
dReturn_if_fail (entry != NULL);
- if (Op == IOClose) {
+ _MSG("__a_Cache_process_dbuf__\n");
+
+ if (Op == IORead) {
+ /*
+ * Cache_get_header() will set CA_GotHeader if it has a full header, and
+ * Cache_parse_header() will unset it if the header ends being
+ * merely an informational response from the server (i.e., 100 Continue)
+ */
+ for (offset = 0; !(entry->Flags & CA_GotHeader) &&
+ (len = Cache_get_header(entry, buf + offset, buf_size - offset));
+ Cache_parse_header(entry) ) {
+ offset += len;
+ }
+
+ if (entry->Flags & CA_GotHeader) {
+ str = buf + offset;
+ len = buf_size - offset;
+ entry->TransferSize += len;
+ dstr1 = dstr2 = dstr3 = NULL;
+
+ /* Decode arrived data (<= 3 stages) */
+ if (entry->TransferDecoder) {
+ dstr1 = a_Decode_process(entry->TransferDecoder, str, len);
+ str = dstr1->str;
+ len = dstr1->len;
+ }
+ if (entry->ContentDecoder) {
+ dstr2 = a_Decode_process(entry->ContentDecoder, str, len);
+ str = dstr2->str;
+ len = dstr2->len;
+ }
+ dStr_append_l(entry->Data, str, len);
+ if (entry->CharsetDecoder && entry->UTF8Data) {
+ dstr3 = a_Decode_process(entry->CharsetDecoder, str, len);
+ dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len);
+ }
+ dStr_free(dstr1, 1);
+ dStr_free(dstr2, 1);
+ dStr_free(dstr3, 1);
+
+ if (entry->Data->len)
+ entry->Flags &= ~CA_IsEmpty;
+
+ entry = Cache_process_queue(entry);
+ }
+ } else if (Op == IOClose) {
+ if ((entry->ExpectedSize || entry->TransferSize) &&
+ entry->TypeHdr == NULL) {
+ MSG_HTTP("Message with a body lacked Content-Type header.\n");
+ }
if ((entry->Flags & CA_GotLength) &&
(entry->ExpectedSize != entry->TransferSize)) {
MSG_HTTP("Content-Length does NOT match message body,\n"
@@ -818,65 +896,16 @@ void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
entry->ContentDecoder = NULL;
}
dStr_fit(entry->Data); /* fit buffer size! */
- Cache_process_queue(entry);
- if (entry->Flags & CA_GotHeader) {
- Cache_unref_data(entry);
+
+ if ((entry = Cache_process_queue(entry))) {
+ if (entry->Flags & CA_GotHeader) {
+ Cache_unref_data(entry);
+ }
}
- return;
} else if (Op == IOAbort) {
/* unused */
MSG("a_Cache_process_dbuf Op = IOAbort; not implemented!\n");
- return;
- }
-
- /*
- * Cache_get_header() will set CA_GotHeader if it has a full header, and
- * Cache_parse_header() will unset it if the header turns out to have been
- * merely an informational response from the server (i.e., 100 Continue)
- */
- while (!(entry->Flags & CA_GotHeader) &&
- (len = Cache_get_header(entry, buf + offset, buf_size - offset))) {
- offset += len;
- /* Let's scan, allocate, and set things according to header info */
- Cache_parse_header(entry);
}
-
- if (!(entry->Flags & CA_GotHeader))
- return;
-
- str = buf + offset;
- len = buf_size - offset;
- entry->TransferSize += len;
-
- if (entry->TransferDecoder) {
- Dstr *dbuf = a_Decode_process(entry->TransferDecoder, str, len);
- str = dbuf->str;
- len = dbuf->len;
- dStr_free(dbuf, 0);
- }
- if (entry->ContentDecoder) {
- Dstr *dbuf = a_Decode_process(entry->ContentDecoder, str, len);
- if (entry->TransferDecoder)
- dFree((char *)str);
- str = dbuf->str;
- len = dbuf->len;
- dStr_free(dbuf, 0);
- }
- dStr_append_l(entry->Data, str, len);
-
- if (entry->UTF8Data) {
- Dstr *dbuf = a_Decode_process(entry->CharsetDecoder, str, len);
- dStr_append_l(entry->UTF8Data, dbuf->str, dbuf->len);
- dStr_free(dbuf, 1);
- }
-
- if (entry->TransferDecoder || entry->ContentDecoder)
- dFree((char *)str);
-
- if (entry->Data->len)
- entry->Flags &= ~CA_IsEmpty;
-
- Cache_process_queue(entry);
}
/*
@@ -912,7 +941,7 @@ static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
NewUrl = a_Url_new(URL_STR_(entry->Location), URL_STR_(entry->Url));
if (entry->Flags & CA_TempRedirect)
a_Url_set_flags(NewUrl, URL_FLAGS(NewUrl) | URL_E2EQuery);
- a_Nav_push(bw, NewUrl);
+ a_Nav_push(bw, NewUrl, entry->Url);
a_Url_free(NewUrl);
} else {
/* Sub entity redirection (most probably an image) */
@@ -926,11 +955,55 @@ static int Cache_redirect(CacheEntry_t *entry, int Flags, BrowserWindow *bw)
return 0;
}
+typedef struct {
+ Dlist *auth;
+ DilloUrl *url;
+ BrowserWindow *bw;
+} CacheAuthData_t;
+
+/*
+ * Ask for user/password and reload the page.
+ */
+static void Cache_auth_callback(void *vdata)
+{
+ CacheAuthData_t *data = (CacheAuthData_t *)vdata;
+ if (a_Auth_do_auth(data->auth, data->url))
+ a_Nav_reload(data->bw);
+ Cache_auth_free(data->auth);
+ a_Url_free(data->url);
+ dFree(data);
+ Cache_auth_entry(NULL, NULL);
+ a_Timeout_remove();
+}
+
+/*
+ * Set a timeout function to ask for user/password.
+ */
+static void Cache_auth_entry(CacheEntry_t *entry, BrowserWindow *bw)
+{
+ static int busy = 0;
+ CacheAuthData_t *data;
+
+ if (!entry) {
+ busy = 0;
+ } else if (busy) {
+ MSG_WARN("Cache_auth_entry: caught busy!\n");
+ } else if (entry->Auth) {
+ busy = 1;
+ data = dNew(CacheAuthData_t, 1);
+ data->auth = entry->Auth;
+ data->url = a_Url_dup(entry->Url);
+ data->bw = bw;
+ entry->Auth = NULL;
+ a_Timeout_add(0.0, Cache_auth_callback, data);
+ }
+}
+
/*
* Check whether a URL scheme is downloadable.
* Return: 1 enabled, 0 disabled.
*/
-static int Cache_download_enabled(const DilloUrl *url)
+int a_Cache_download_enabled(const DilloUrl *url)
{
if (!dStrcasecmp(URL_SCHEME(url), "http") ||
!dStrcasecmp(URL_SCHEME(url), "https") ||
@@ -961,6 +1034,24 @@ static void Cache_null_client(int Op, CacheClient_t *Client)
return;
}
+typedef struct {
+ BrowserWindow *bw;
+ DilloUrl *url;
+} Cache_savelink_t;
+
+/*
+ * Save link from behind a timeout so that Cache_process_queue() can
+ * get on with its work.
+ */
+static void Cache_savelink_cb(void *vdata)
+{
+ Cache_savelink_t *data = (Cache_savelink_t*) vdata;
+
+ a_UIcmd_save_link(data->bw, data->url);
+ a_Url_free(data->url);
+ dFree(data);
+}
+
/*
* Update cache clients for a single cache-entry
* Tasks:
@@ -969,9 +1060,11 @@ static void Cache_null_client(int Op, CacheClient_t *Client)
* - Remove clients when done
* - Call redirect handler
*
+ * Return: Cache entry, which may be NULL if it has been removed.
+ *
* TODO: Implement CA_Abort Op in client callback
*/
-static void Cache_process_queue(CacheEntry_t *entry)
+static CacheEntry_t *Cache_process_queue(CacheEntry_t *entry)
{
uint_t i;
int st;
@@ -988,7 +1081,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (Busy)
MSG_ERR("FATAL!: >>>> Cache_process_queue Caught busy!!! <<<<\n");
if (!(entry->Flags & CA_GotHeader))
- return;
+ return entry;
if (!(entry->Flags & CA_GotContentType)) {
st = a_Misc_get_content_type_from_data(
entry->Data->str, entry->Data->len, &Type);
@@ -1002,7 +1095,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
entry->TypeDet = dStrdup(Type);
entry->Flags |= CA_GotContentType;
} else
- return; /* i.e., wait for more data */
+ return entry; /* i.e., wait for more data */
}
Busy = TRUE;
@@ -1020,6 +1113,7 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (TypeMismatch) {
a_UIcmd_set_msg(Client_bw,"HTTP warning: Content-Type '%s' "
"doesn't match the real data.", entry->TypeHdr);
+ OfferDownload = TRUE;
}
if (entry->Flags & CA_Redirect) {
if (!Client->Callback) {
@@ -1046,13 +1140,13 @@ static void Cache_process_queue(CacheEntry_t *entry)
if (TypeMismatch) {
AbortEntry = TRUE;
} else {
- const char *content_type = Cache_current_content_type(entry);
- st = a_Web_dispatch_by_type(content_type, ClientWeb,
+ const char *curr_type = Cache_current_content_type(entry);
+ st = a_Web_dispatch_by_type(curr_type, ClientWeb,
&Client->Callback, &Client->CbData);
if (st == -1) {
/* MIME type is not viewable */
if (ClientWeb->flags & WEB_RootUrl) {
- MSG("Content-Type '%s' not viewable.\n", content_type);
+ MSG("Content-Type '%s' not viewable.\n", curr_type);
/* prepare a download offer... */
AbortEntry = OfferDownload = TRUE;
} else {
@@ -1081,8 +1175,10 @@ static void Cache_process_queue(CacheEntry_t *entry)
if ((Client->BufSize = data->len) > 0) {
Client->Buf = data->str;
(Client->Callback)(CA_Send, Client);
- if (ClientWeb->flags & WEB_RootUrl)
- a_UIcmd_set_page_prog(Client_bw, data->len, 1);
+ if (ClientWeb->flags & WEB_RootUrl) {
+ /* show size of page received */
+ a_UIcmd_set_page_prog(Client_bw, entry->Data->len, 1);
+ }
}
/* Remove client when done */
@@ -1107,11 +1203,20 @@ static void Cache_process_queue(CacheEntry_t *entry)
/* Abort the entry, remove it from cache, and maybe offer download. */
DilloUrl *url = a_Url_dup(entry->Url);
a_Capi_conn_abort_by_url(url);
- Cache_entry_remove(entry, NULL);
- if (OfferDownload && Cache_download_enabled(url)) {
- a_UIcmd_save_link(Client_bw, url);
+ entry = NULL;
+ if (OfferDownload) {
+ /* Remove entry when 'conn' is already done */
+ Cache_entry_remove(NULL, url);
+ if (a_Cache_download_enabled(url)) {
+ Cache_savelink_t *data = dNew(Cache_savelink_t, 1);
+ data->bw = Client_bw;
+ data->url = a_Url_dup(url);
+ a_Timeout_add(0.0, Cache_savelink_cb, data);
+ }
}
a_Url_free(url);
+ } else if (entry->Auth && (entry->Flags & CA_GotData)) {
+ Cache_auth_entry(entry, Client_bw);
}
/* Trigger cleanup when there are no cache clients */
@@ -1122,21 +1227,19 @@ static void Cache_process_queue(CacheEntry_t *entry)
Busy = FALSE;
_MSG("QueueSize ====> %d\n", dList_length(ClientQueue));
+ return entry;
}
/*
* Callback function for Cache_delayed_process_queue.
*/
-static void Cache_delayed_process_queue_callback(void *data)
+static void Cache_delayed_process_queue_callback()
{
CacheEntry_t *entry;
while ((entry = (CacheEntry_t *)dList_nth_data(DelayedQueue, 0))) {
Cache_ref_data(entry);
- Cache_process_queue(entry);
- if (entry != dList_nth_data(DelayedQueue, 0)) {
- /* Cache_process_queue() has removed the entry! */
- } else {
+ if ((entry = Cache_process_queue(entry))) {
Cache_unref_data(entry);
dList_remove(DelayedQueue, entry);
}
@@ -1189,10 +1292,23 @@ CacheClient_t *a_Cache_client_get_if_unique(int Key)
void a_Cache_stop_client(int Key)
{
CacheClient_t *Client;
+ CacheEntry_t *entry;
+ DICacheEntry *DicEntry;
+ /* The client can be in both queues at the same time */
if ((Client = dList_find_custom(ClientQueue, INT2VOIDP(Key),
Cache_client_by_key_cmp))) {
+ /* Dicache */
+ if ((DicEntry = a_Dicache_get_entry(Client->Url, Client->Version)))
+ a_Dicache_unref(Client->Url, Client->Version);
+
+ /* DelayedQueue */
+ if ((entry = Cache_entry_search(Client->Url)))
+ dList_remove(DelayedQueue, entry);
+
+ /* Main queue */
Cache_client_dequeue(Client, NULLKey);
+
} else {
_MSG("WARNING: Cache_stop_client, nonexistent client\n");
}
@@ -1213,7 +1329,7 @@ void a_Cache_freeall(void)
/* Remove every cache entry */
while ((data = dList_nth_data(CachedURLs, 0))) {
- dList_remove(CachedURLs, data);
+ dList_remove_fast(CachedURLs, data);
Cache_entry_free(data);
}
/* Remove the cache list */
diff --git a/src/cache.h b/src/cache.h
index 6099447c..c01bec55 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -46,6 +46,7 @@ typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
struct _CacheClient {
int Key; /* Primary Key for this client */
const DilloUrl *Url; /* Pointer to a cache entry Url */
+ int Version; /* Dicache version of this Url (0 if not used) */
void *Buf; /* Pointer to cache-data */
uint_t BufSize; /* Valid size of cache-data */
CA_Callback_t Callback; /* Client function */
@@ -62,11 +63,12 @@ int a_Cache_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Cache_unref_buf(const DilloUrl *Url);
const char *a_Cache_get_content_type(const DilloUrl *url);
const char *a_Cache_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force);
+ const char *from);
uint_t a_Cache_get_flags(const DilloUrl *url);
+uint_t a_Cache_get_flags_with_redirection(const DilloUrl *url);
void a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
const DilloUrl *Url);
-void a_Cache_entry_inject(const DilloUrl *Url, Dstr *data_ds);
+int a_Cache_download_enabled(const DilloUrl *url);
void a_Cache_entry_remove_by_url(DilloUrl *url);
void a_Cache_freeall(void);
CacheClient_t *a_Cache_client_get_if_unique(int Key);
diff --git a/src/capi.c b/src/capi.c
index efa2cd93..3df48628 100644
--- a/src/capi.c
+++ b/src/capi.c
@@ -22,7 +22,6 @@
#include "IO/IO.h" /* for IORead &friends */
#include "IO/Url.h"
#include "chain.h"
-#include "list.h"
#include "history.h"
#include "nav.h"
#include "dpiapi.h"
@@ -56,10 +55,10 @@ enum {
* Local data
*/
/* Data list for active dpi connections */
-static capi_conn_t **DpiConn = NULL;
-static int DpiConnSize;
-static int DpiConnMax = 4;
-
+static Dlist *CapiConns; /* Data list for active connections; it holds
+ * pointers to capi_conn_t structures. */
+/* Last URL asked for view source */
+static DilloUrl *CapiVsUrl = NULL;
/*
* Forward declarations
@@ -75,7 +74,9 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
*/
void a_Capi_init(void)
{
- /* nothing to do for capi yet, just for cache */
+ /* create an empty list */
+ CapiConns = dList_new(32);
+ /* init cache */
a_Cache_init();
}
@@ -85,7 +86,7 @@ void a_Capi_init(void)
* Create a new connection data structure
*/
static capi_conn_t *
- Capi_conn_new(DilloUrl *url, void *bw, char *server, char *datastr)
+ Capi_conn_new(const DilloUrl *url, void *bw, char *server, char *datastr)
{
capi_conn_t *conn;
@@ -103,15 +104,22 @@ static capi_conn_t *
}
/*
+ * Validate a capi_conn_t pointer.
+ * Return value: NULL if not valid, conn otherwise.
+ */
+static capi_conn_t *Capi_conn_valid(capi_conn_t *conn)
+{
+ return dList_find(CapiConns, conn);
+}
+
+/*
* Increment the reference count and add to the list if not present
*/
static void Capi_conn_ref(capi_conn_t *conn)
{
if (++conn->Ref == 1) {
/* add the connection data to list */
- a_List_add(DpiConn, DpiConnSize, DpiConnMax);
- DpiConn[DpiConnSize] = conn;
- DpiConnSize++;
+ dList_append(CapiConns, (void *)conn);
}
_MSG(" Capi_conn_ref #%d %p\n", conn->Ref, conn);
}
@@ -121,26 +129,30 @@ static void Capi_conn_ref(capi_conn_t *conn)
*/
static void Capi_conn_unref(capi_conn_t *conn)
{
- int i, j;
-
_MSG(" Capi_conn_unref #%d %p\n", conn->Ref - 1, conn);
+ /* We may validate conn here, but it doesn't *seem* necessary */
if (--conn->Ref == 0) {
- for (i = 0; i < DpiConnSize; ++i) {
- if (DpiConn[i] == conn) {
- /* remove conn preserving the list order */
- for (j = i; j + 1 < DpiConnSize; ++j)
- DpiConn[j] = DpiConn[j + 1];
- --DpiConnSize;
- /* free dynamic memory */
- a_Url_free(conn->url);
- dFree(conn->server);
- dFree(conn->datastr);
- dFree(conn);
- break;
- }
- }
+ /* remove conn preserving the list order */
+ dList_remove(CapiConns, (void *)conn);
+ /* free dynamic memory */
+ a_Url_free(conn->url);
+ dFree(conn->server);
+ dFree(conn->datastr);
+ dFree(conn);
}
+ _MSG(" Capi_conn_unref CapiConns=%d\n", dList_length(CapiConns));
+}
+
+/*
+ * Compare function for searching a conn by server string
+ */
+static int Capi_conn_by_server_cmp(const void *v1, const void *v2)
+{
+ const capi_conn_t *node = v1;
+ const char *server = v2;
+ dReturn_val_if_fail(node && node->server && server, 1);
+ return strcmp(node->server, server);
}
/*
@@ -148,13 +160,7 @@ static void Capi_conn_unref(capi_conn_t *conn)
*/
static capi_conn_t *Capi_conn_find(char *server)
{
- int i;
-
- for (i = 0; i < DpiConnSize; ++i)
- if (strcmp(server, DpiConn[i]->server) == 0)
- return DpiConn[i];
-
- return NULL;
+ return dList_find_custom(CapiConns, (void*)server, Capi_conn_by_server_cmp);
}
/*
@@ -164,12 +170,15 @@ static void Capi_conn_resume(void)
{
int i;
DataBuf *dbuf;
+ capi_conn_t *conn;
- for (i = 0; i < DpiConnSize; ++i) {
- if (DpiConn[i]->Flags & PENDING) {
- capi_conn_t *conn = DpiConn[i];
+ for (i = 0; i < dList_length(CapiConns); ++i) {
+ conn = dList_nth_data (CapiConns, i);
+ if (conn->Flags & PENDING) {
dbuf = a_Chain_dbuf_new(conn->datastr,(int)strlen(conn->datastr), 0);
- a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
+ if (conn->InfoSend) {
+ a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
+ }
dFree(dbuf);
conn->Flags &= ~PENDING;
}
@@ -178,14 +187,18 @@ static void Capi_conn_resume(void)
/*
* Abort the connection for a given url, using its CCC.
+ * (OpAbort 2,BCK removes the cache entry)
+ * TODO: when conn is already done, the cache entry isn't removed.
+ * This may be wrong and needs a revision.
*/
void a_Capi_conn_abort_by_url(const DilloUrl *url)
{
int i;
+ capi_conn_t *conn;
- for (i = 0; i < DpiConnSize; ++i) {
- if (a_Url_cmp(DpiConn[i]->url, url) == 0) {
- capi_conn_t *conn = DpiConn[i];
+ for (i = 0; i < dList_length(CapiConns); ++i) {
+ conn = dList_nth_data (CapiConns, i);
+ if (a_Url_cmp(conn->url, url) == 0) {
if (conn->InfoSend) {
a_Capi_ccc(OpAbort, 1, BCK, conn->InfoSend, NULL, NULL);
}
@@ -200,21 +213,35 @@ void a_Capi_conn_abort_by_url(const DilloUrl *url)
/* ------------------------------------------------------------------------- */
/*
- * Safety test: only allow dpi-urls from dpi-generated pages.
+ * Store the last URL requested by "view source"
*/
-static int Capi_dpi_verify_request(DilloWeb *web)
+void a_Capi_set_vsource_url(const DilloUrl *url)
{
- DilloUrl *referer;
+ a_Url_free(CapiVsUrl);
+ CapiVsUrl = a_Url_dup(url);
+}
+
+/*
+ * Safety test: only allow GET|POST dpi-urls from dpi-generated pages.
+ */
+int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url)
+{
+ const DilloUrl *referer;
int allow = FALSE;
- /* test POST and GET */
- if (dStrcasecmp(URL_SCHEME(web->url), "dpi") == 0 &&
- URL_FLAGS(web->url) & (URL_Post + URL_Get)) {
- /* only allow dpi requests from dpi-generated urls */
- if (a_Nav_stack_size(web->bw)) {
- referer = a_History_get_url(NAV_TOP_UIDX(web->bw));
- if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) {
- allow = TRUE;
+ if (dStrcasecmp(URL_SCHEME(url), "dpi") == 0) {
+ if (!(URL_FLAGS(url) & (URL_Post + URL_Get))) {
+ allow = TRUE;
+ } else if (!(URL_FLAGS(url) & URL_Post) &&
+ strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0) {
+ allow = TRUE;
+ } else {
+ /* only allow GET&POST dpi-requests from dpi-generated urls */
+ if (a_Nav_stack_size(bw)) {
+ referer = a_History_get_url(NAV_TOP_UIDX(bw));
+ if (dStrcasecmp(URL_SCHEME(referer), "dpi") == 0) {
+ allow = TRUE;
+ }
}
}
} else {
@@ -222,10 +249,10 @@ static int Capi_dpi_verify_request(DilloWeb *web)
}
if (!allow) {
- MSG("Capi_dpi_verify_request: Permission Denied!\n");
- MSG(" URL_STR : %s\n", URL_STR(web->url));
- if (URL_FLAGS(web->url) & URL_Post) {
- MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(web->url), 1024));
+ MSG("a_Capi_dpi_verify_request: Permission Denied!\n");
+ MSG(" URL_STR : %s\n", URL_STR(url));
+ if (URL_FLAGS(url) & URL_Post) {
+ MSG(" URL_DATA: %s\n", dStr_printable(URL_DATA(url), 1024));
}
}
return allow;
@@ -265,7 +292,6 @@ static int Capi_url_uses_dpi(DilloUrl *url, char **server_ptr)
/*
* Build the dpip command tag, according to URL and server.
- * TODO: make it PROXY-aware (AFAIS, it should be easy)
*/
static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
{
@@ -273,10 +299,20 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
if (strcmp(server, "proto.https") == 0) {
/* Let's be kind and make the HTTP query string for the dpi */
+ char *proxy_connect = a_Http_make_connect_str(web->url);
Dstr *http_query = a_Http_make_query_str(web->url, FALSE);
/* BUG: embedded NULLs in query data will truncate message */
- cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
- "open_url", URL_STR(web->url), http_query->str);
+ if (proxy_connect) {
+ const char *proxy_urlstr = a_Http_get_proxy_urlstr();
+ cmd = a_Dpip_build_cmd("cmd=%s proxy_url=%s proxy_connect=%s "
+ "url=%s query=%s", "open_url", proxy_urlstr,
+ proxy_connect, URL_STR(web->url),
+ http_query->str);
+ } else {
+ cmd = a_Dpip_build_cmd("cmd=%s url=%s query=%s",
+ "open_url", URL_STR(web->url),http_query->str);
+ }
+ dFree(proxy_connect);
dStr_free(http_query, 1);
} else if (strcmp(server, "downloads") == 0) {
@@ -292,6 +328,84 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
}
/*
+ * Send the requested URL's source to the "view source" dpi
+ */
+static void Capi_dpi_send_source(BrowserWindow *bw, DilloUrl *url)
+{
+ char *p, *buf, *cmd, size_str[32], *server="vsource";
+ int buf_size;
+
+ if (!(p = strchr(URL_STR(url), ':')) || !(p = strchr(p + 1, ':')))
+ return;
+
+ if (a_Capi_get_buf(CapiVsUrl, &buf, &buf_size)) {
+ /* send the page's source to this dpi connection */
+ snprintf(size_str, 32, "%d", buf_size);
+ cmd = a_Dpip_build_cmd("cmd=%s url=%s data_size=%s",
+ "start_send_page", URL_STR(url), size_str);
+ a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
+ a_Capi_dpi_send_data(url, bw, buf, buf_size, server, 0);
+ } else {
+ cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
+ "DpiError", "Page is NOT cached");
+ a_Capi_dpi_send_cmd(NULL, bw, cmd, server, 0);
+ }
+ dFree(cmd);
+}
+
+/*
+ * When dillo wants to open an URL, this can be either due to user action
+ * (e.g., typing in an URL, clicking a link), or automatic (HTTP header
+ * indicates redirection, META HTML tag with refresh attribute and 0 delay,
+ * and images and stylesheets on an HTML page when autoloading is enabled).
+ *
+ * For a user request, the action will be permitted.
+ * For an automatic request, permission to load depends on the filter set
+ * by the user.
+ */
+static bool_t Capi_filters_allow(const DilloUrl *wanted,
+ const DilloUrl *requester)
+{
+ bool_t ret;
+
+ if (requester == NULL) {
+ /* request made by user */
+ ret = TRUE;
+ } else {
+ switch (prefs.filter_auto_requests) {
+ case PREFS_FILTER_SAME_DOMAIN:
+ {
+ const char *req_host = URL_HOST(requester),
+ *want_host = URL_HOST(wanted),
+ *req_suffix,
+ *want_suffix;
+ if (want_host[0] == '\0') {
+ ret = (req_host[0] == '\0' ||
+ !dStrcasecmp(URL_SCHEME(wanted), "data")) ? TRUE : FALSE;
+ } else {
+ /* This will regard "www.dillo.org" and "www.dillo.org." as
+ * different, but it doesn't seem worth caring about.
+ */
+ req_suffix = a_Url_host_find_public_suffix(req_host);
+ want_suffix = a_Url_host_find_public_suffix(want_host);
+
+ ret = dStrcasecmp(req_suffix, want_suffix) == 0;
+ }
+
+ MSG("Capi_filters_allow: from %s to %s: %s\n", req_host, want_host,
+ ret ? "ALLOWED" : "DENIED");
+ break;
+ }
+ case PREFS_FILTER_ALLOW_ALL:
+ default:
+ ret = TRUE;
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
* Most used function for requesting a URL.
* TODO: clean up the ad-hoc bindings with an API that allows dynamic
* addition of new plugins.
@@ -301,12 +415,15 @@ static char *Capi_dpi_build_cmd(DilloWeb *web, char *server)
*/
int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
{
- capi_conn_t *conn;
int reload;
char *cmd, *server;
+ capi_conn_t *conn = NULL;
const char *scheme = URL_SCHEME(web->url);
int safe = 0, ret = 0, use_cache = 0;
+ dReturn_val_if_fail((a_Capi_get_flags(web->url) & CAPI_IsCached) ||
+ Capi_filters_allow(web->url, web->requester), 0);
+
/* reload test */
reload = (!(a_Capi_get_flags(web->url) & CAPI_IsCached) ||
(URL_FLAGS(web->url) & URL_E2EQuery));
@@ -314,35 +431,43 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
if (web->flags & WEB_Download) {
/* download request: if cached save from cache, else
* for http, ftp or https, use the downloads dpi */
- if (a_Capi_get_flags(web->url) & CAPI_IsCached) {
- if (web->filename && (web->stream = fopen(web->filename, "w"))) {
- use_cache = 1;
- }
- } else {
- if (!dStrcasecmp(scheme, "https") ||
- !dStrcasecmp(scheme, "http") ||
- !dStrcasecmp(scheme, "ftp")) {
- server = "downloads";
- cmd = Capi_dpi_build_cmd(web, server);
- a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
- dFree(cmd);
+ if (a_Capi_get_flags_with_redirection(web->url) & CAPI_IsCached) {
+ if (web->filename) {
+ if ((web->stream = fopen(web->filename, "w"))) {
+ use_cache = 1;
+ } else {
+ MSG_WARN("Cannot open \"%s\" for writing.\n", web->filename);
+ }
}
+ } else if (a_Cache_download_enabled(web->url)) {
+ server = "downloads";
+ cmd = Capi_dpi_build_cmd(web, server);
+ a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
+ dFree(cmd);
}
} else if (Capi_url_uses_dpi(web->url, &server)) {
/* dpi request */
- if ((safe = Capi_dpi_verify_request(web))) {
+ if ((safe = a_Capi_dpi_verify_request(web->bw, web->url))) {
if (dStrcasecmp(scheme, "dpi") == 0) {
- /* make "dpi:/" prefixed urls always reload. */
- a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
- reload = 1;
+ if (strcmp(server, "vsource") == 0) {
+ /* don't reload the "view source" page */
+ } else {
+ /* make the other "dpi:/" prefixed urls always reload. */
+ a_Url_set_flags(web->url, URL_FLAGS(web->url) | URL_E2EQuery);
+ reload = 1;
+ }
}
if (reload) {
a_Capi_conn_abort_by_url(web->url);
/* Send dpip command */
+ _MSG("a_Capi_open_url, reload url='%s'\n", URL_STR(web->url));
cmd = Capi_dpi_build_cmd(web, server);
a_Capi_dpi_send_cmd(web->url, web->bw, cmd, server, 1);
dFree(cmd);
+ if (strcmp(server, "vsource") == 0) {
+ Capi_dpi_send_source(web->bw, web->url);
+ }
}
use_cache = 1;
}
@@ -354,6 +479,9 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
a_Capi_conn_abort_by_url(web->url);
/* create a new connection and start the CCC operations */
conn = Capi_conn_new(web->url, web->bw, "http", "none");
+ /* start the reception branch before the query one because the DNS
+ * may callback immediatly. This may avoid a race condition. */
+ a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, "http");
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, web);
}
use_cache = 1;
@@ -364,7 +492,10 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
}
if (use_cache) {
- ret = a_Cache_open_url(web, Call, CbData);
+ if (!conn || (conn && Capi_conn_valid(conn))) {
+ /* not aborted, let's continue... */
+ ret = a_Cache_open_url(web, Call, CbData);
+ }
} else {
a_Web_free(web);
}
@@ -372,12 +503,11 @@ int a_Capi_open_url(DilloWeb *web, CA_Callback_t Call, void *CbData)
}
/*
- * Return status information of an URL's content-transfer process.
+ * Convert cache-defined flags to Capi ones.
*/
-int a_Capi_get_flags(const DilloUrl *Url)
+static int Capi_map_cache_flags(uint_t flags)
{
int status = 0;
- uint_t flags = a_Cache_get_flags(Url);
if (flags) {
status |= CAPI_IsCached;
@@ -394,6 +524,26 @@ int a_Capi_get_flags(const DilloUrl *Url)
}
/*
+ * Return status information of an URL's content-transfer process.
+ */
+int a_Capi_get_flags(const DilloUrl *Url)
+{
+ uint_t flags = a_Cache_get_flags(Url);
+ int status = flags ? Capi_map_cache_flags(flags) : 0;
+ return status;
+}
+
+/*
+ * Same as a_Capi_get_flags() but following redirections.
+ */
+int a_Capi_get_flags_with_redirection(const DilloUrl *Url)
+{
+ uint_t flags = a_Cache_get_flags_with_redirection(Url);
+ int status = flags ? Capi_map_cache_flags(flags) : 0;
+ return status;
+}
+
+/*
* Get the cache's buffer for the URL, and its size.
* Return: 1 cached, 0 not cached.
*/
@@ -419,20 +569,21 @@ const char *a_Capi_get_content_type(const DilloUrl *url)
}
/*
- * Set the Content-Type for the URL.
+ * Set the Content-Type for the URL.
*/
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force)
+ const char *from)
{
- return a_Cache_set_content_type(url, ctype, force);
+ return a_Cache_set_content_type(url, ctype, from);
}
/*
- * Send a dpi cmd.
- * (For instance: add_bookmark, open_url, send_preferences, ...)
+ * Send data to a dpi (e.g. add_bookmark, open_url, send_preferences, ...)
+ * Most of the time we send dpi commands, but it also serves for raw data
+ * as with "view source".
*/
-int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
- int flags)
+int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
+ char *data, int data_sz, char *server, int flags)
{
capi_conn_t *conn;
DataBuf *dbuf;
@@ -441,8 +592,9 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
/* open a new connection to server */
/* Create a new connection data struct and add it to the list */
- conn = Capi_conn_new(url, bw, server, cmd);
+ conn = Capi_conn_new(url, bw, server, data);
/* start the CCC operations */
+ a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), conn, server);
a_Capi_ccc(OpStart, 1, BCK, a_Chain_new(), conn, server);
} else {
@@ -450,11 +602,11 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
conn = Capi_conn_find(server);
if (conn) {
/* found */
- dbuf = a_Chain_dbuf_new(cmd, (int)strlen(cmd), 0);
+ dbuf = a_Chain_dbuf_new(data, data_sz, 0);
a_Capi_ccc(OpSend, 1, BCK, conn->InfoSend, dbuf, NULL);
dFree(dbuf);
} else {
- MSG(" ERROR: [a_Capi_dpi_send_cmd] No open connection found\n");
+ MSG(" ERROR: [a_Capi_dpi_send_data] No open connection found\n");
}
}
@@ -462,6 +614,16 @@ int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
}
/*
+ * Send a dpi cmd.
+ * (For instance: add_bookmark, open_url, send_preferences, ...)
+ */
+int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
+ int flags)
+{
+ return a_Capi_dpi_send_data(url, bw, cmd, strlen(cmd), server, flags);
+}
+
+/*
* Remove a client from the cache client queue.
* force = also abort the CCC if this is the last client.
*/
@@ -469,7 +631,11 @@ void a_Capi_stop_client(int Key, int force)
{
CacheClient_t *Client;
- if (force && (Client = a_Cache_client_get_if_unique(Key))) {
+ _MSG("a_Capi_stop_client: force=%d\n", force);
+
+ Client = a_Cache_client_get_if_unique(Key);
+ if (Client && (force || Client->BufSize == 0)) {
+ /* remove empty entries too */
a_Capi_conn_abort_by_url(Client->Url);
}
a_Cache_stop_client(Key);
@@ -525,18 +691,17 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 1 FWD */
/* Command sending branch (status) */
switch (Op) {
case OpSend:
if (!Data2) {
MSG_WARN("Capi.c: Opsend [1F] Data2 = NULL\n");
- } else if (strcmp(Data2, "SockFD") == 0) {
- /* start the receiving branch */
- capi_conn_t *conn = Info->LocalKey;
+ } else if (strcmp(Data2, "FD") == 0) {
+ conn = Info->LocalKey;
conn->SockFD = *(int*)Data1;
- a_Capi_ccc(OpStart, 2, BCK, a_Chain_new(), Info->LocalKey,
- conn->server);
+ /* communicate the FD through the answer branch */
+ a_Capi_ccc(OpSend, 2, BCK, conn->InfoRecv, &conn->SockFD, "FD");
} else if (strcmp(Data2, "DpidOK") == 0) {
/* resume pending dpi requests */
Capi_conn_resume();
@@ -545,13 +710,19 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
case OpAbort:
conn = Info->LocalKey;
conn->InfoSend = NULL;
- /* remove the cache entry for this URL */
- a_Cache_entry_remove_by_url(conn->url);
- if (Data2 && !strcmp(Data2, "DpidERROR"))
- a_UIcmd_set_msg(conn->bw,
- "ERROR: can't start dpid daemon "
- "(URL scheme = '%s')!",
- URL_SCHEME(conn->url));
+ if (Data2) {
+ if (!strcmp(Data2, "DpidERROR")) {
+ a_UIcmd_set_msg(conn->bw,
+ "ERROR: can't start dpid daemon "
+ "(URL scheme = '%s')!",
+ conn->url ? URL_SCHEME(conn->url) : "");
+ } else if (!strcmp(Data2, "Both") && conn->InfoRecv) {
+ /* abort the other branch too */
+ a_Capi_ccc(OpAbort, 2, BCK, conn->InfoRecv, NULL, NULL);
+ }
+ }
+ /* if URL == expect-url */
+ a_Nav_cancel_expect_if_eq(conn->bw, conn->url);
/* finish conn */
Capi_conn_unref(conn);
dFree(Info);
@@ -564,21 +735,29 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
} else if (Branch == 2) {
if (Dir == BCK) {
- /* Server listening branch (status)
- * (Data1 = conn; Data2 = {"HttpFD" | "DpiFD"}) */
+ /* Answer branch */
switch (Op) {
case OpStart:
+ /* Data1 = conn; Data2 = {"http" | "<dpi server name>"} */
conn = Data1;
Capi_conn_ref(conn);
Info->LocalKey = conn;
conn->InfoRecv = Info;
a_Chain_link_new(Info, a_Capi_ccc, BCK, a_Dpi_ccc, 2, 2);
- a_Chain_bcb(OpStart, Info, &conn->SockFD, Data2);
+ a_Chain_bcb(OpStart, Info, NULL, Data2);
+ break;
+ case OpSend:
+ /* Data1 = FD */
+ if (Data2 && strcmp(Data2, "FD") == 0) {
+ a_Chain_bcb(OpSend, Info, Data1, Data2);
+ }
break;
case OpAbort:
conn = Info->LocalKey;
conn->InfoRecv = NULL;
a_Chain_bcb(OpAbort, Info, NULL, NULL);
+ /* remove the cache entry for this URL */
+ a_Cache_entry_remove_by_url(conn->url);
Capi_conn_unref(conn);
dFree(Info);
break;
@@ -586,7 +765,7 @@ void a_Capi_ccc(int Op, int Branch, int Dir, ChainLink *Info,
MSG_WARN("Unused CCC\n");
break;
}
- } else { /* FWD */
+ } else { /* 2 FWD */
/* Server listening branch */
switch (Op) {
case OpSend:
diff --git a/src/capi.h b/src/capi.h
index c30dc6cf..8a0c7095 100644
--- a/src/capi.h
+++ b/src/capi.h
@@ -27,10 +27,15 @@ int a_Capi_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Capi_unref_buf(const DilloUrl *Url);
const char *a_Capi_get_content_type(const DilloUrl *url);
const char *a_Capi_set_content_type(const DilloUrl *url, const char *ctype,
- bool_t force);
+ const char *from);
int a_Capi_get_flags(const DilloUrl *Url);
+int a_Capi_get_flags_with_redirection(const DilloUrl *Url);
+int a_Capi_dpi_verify_request(BrowserWindow *bw, DilloUrl *url);
+int a_Capi_dpi_send_data(const DilloUrl *url, void *bw,
+ char *data, int data_sz, char *server, int flags);
int a_Capi_dpi_send_cmd(DilloUrl *url, void *bw, char *cmd, char *server,
int flags);
+void a_Capi_set_vsource_url(const DilloUrl *url);
void a_Capi_stop_client(int Key, int force);
void a_Capi_conn_abort_by_url(const DilloUrl *url);
diff --git a/src/chain.c b/src/chain.c
index 37f43a7f..d4098a2d 100644
--- a/src/chain.c
+++ b/src/chain.c
@@ -104,6 +104,7 @@ int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2)
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
} else if (Info->Fcb) {
+ /* flag the caller */
if (Op == OpEnd)
Info->Flags |= CCC_Ended;
else if (Op == OpAbort)
@@ -126,6 +127,7 @@ int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
} else if (Info->Bcb) {
+ /* flag the caller */
if (Op == OpEnd)
Info->Flags |= CCC_Ended;
else if (Op == OpAbort)
@@ -137,6 +139,28 @@ int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2)
return ret;
}
+/*
+ * Issue the backward callback of the 'Info' link and then the
+ * forward callback (used for OpAbort and OpStop).
+ * Return value: 1 if OK, 0 if not operative.
+ */
+int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2)
+{
+ int ret;
+
+ ret = a_Chain_bcb(Op, Info, Data1, Data2);
+ if (ret == 1) {
+ /* we need to clear the flag to reuse this 'Info' ChainLink */
+ if (Op == OpEnd)
+ Info->Flags &= ~CCC_Ended;
+ else if (Op == OpAbort)
+ Info->Flags &= ~CCC_Aborted;
+
+ ret = a_Chain_fcb(Op, Info, Data1, Data2);
+ }
+ return ret;
+}
+
/*
* Allocate and initialize a new DataBuf structure
@@ -153,7 +177,7 @@ DataBuf *a_Chain_dbuf_new(void *buf, int size, int code)
/*
* Check whether the CCC is operative.
* Also used to hook debug information.
- *
+ *
* Return value: 1 if ready to use, 0 if not operative.
*/
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
@@ -166,7 +190,9 @@ int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
if (Info->Flags & (CCC_Ended + CCC_Aborted)) {
/* CCC is not operative */
- MSG_WARN("CCC: call on already finished chain.\n");
+ MSG_WARN("CCC: call on already finished chain. Flags=%s%s\n",
+ Info->Flags & CCC_Ended ? "CCC_Ended " : "",
+ Info->Flags & CCC_Aborted ? "CCC_Aborted" : "");
} else {
ret = 1;
}
diff --git a/src/chain.h b/src/chain.h
index b6b41bd4..fd86557c 100644
--- a/src/chain.h
+++ b/src/chain.h
@@ -69,6 +69,7 @@ ChainLink *a_Chain_link_new(ChainLink *AInfo, ChainFunction_t AFunc,
void a_Chain_unlink(ChainLink *Info, int Direction);
int a_Chain_fcb(int Op, ChainLink *Info, void *Data1, void *Data2);
int a_Chain_bcb(int Op, ChainLink *Info, void *Data1, void *Data2);
+int a_Chain_bfcb(int Op, ChainLink *Info, void *Data1, void *Data2);
int a_Chain_check(char *FuncStr, int Op, int Branch, int Dir,
ChainLink *Info);
diff --git a/src/colors.c b/src/colors.c
index 9a4c8c8e..5b647bb2 100644
--- a/src/colors.c
+++ b/src/colors.c
@@ -204,7 +204,7 @@ static const struct key {
#define NCOLORS (sizeof(color_keyword) / sizeof(struct key))
/*
- * Parse a color in hex (RRGGBB)
+ * Parse a color in hex (RRGGBB) or (RGB)
*
* Return Value:
* parsed color if successful (err = 0),
@@ -219,7 +219,12 @@ static int32_t Color_parse_hex (const char *s, int32_t default_color, int *err)
ret_color = strtol(s, &tail, 16);
if (tail - s == 6)
*err = 0;
- else
+ else if (tail - s == 3) { /* #RGB as allowed by CSS */
+ *err = 0;
+ ret_color = ((ret_color & 0xf00) << 12) | ((ret_color & 0xf00) << 8) |
+ ((ret_color & 0x0f0) << 8) | ((ret_color & 0x0f0) << 4) |
+ ((ret_color & 0x00f) << 4) | ((ret_color & 0x00f) << 0);
+ } else
ret_color = default_color;
return ret_color;
@@ -241,7 +246,7 @@ int32_t a_Color_parse (const char *subtag, int32_t default_color, int *err)
int ret, low, mid, high, st = 1;
/* skip leading spaces */
- for (cp = subtag; isspace(*cp); cp++);
+ for (cp = subtag; dIsspace(*cp); cp++);
ret_color = default_color;
if (*cp == '#') {
diff --git a/src/cookies.c b/src/cookies.c
index 1b336b83..7b9062e2 100644
--- a/src/cookies.c
+++ b/src/cookies.c
@@ -10,10 +10,9 @@
* (at your option) any later version.
*/
-/* Handling of cookies takes place here.
- * This implementation aims to follow RFC 2965:
- * http://www.ietf.org/rfc/rfc2965.txt
- */
+/* Handling of cookies takes place here. */
+
+#include "msg.h"
#ifdef DISABLE_COOKIES
@@ -34,15 +33,13 @@ void a_Cookies_init(void)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
-#include <time.h> /* for time() and time_t */
#include <ctype.h>
+#include <errno.h>
-#include "msg.h"
#include "IO/Url.h"
#include "list.h"
#include "cookies.h"
#include "capi.h"
-#include "dpiapi.h"
#include "../dpip/dpip.h"
@@ -80,14 +77,19 @@ static int Cookie_control_init(void);
static FILE *Cookies_fopen(const char *filename, char *init_str)
{
FILE *F_in;
- int fd;
+ int fd, rc;
if ((F_in = fopen(filename, "r")) == NULL) {
/* Create the file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd != -1) {
- if (init_str)
- write(fd, init_str, strlen(init_str));
+ if (init_str) {
+ rc = write(fd, init_str, strlen(init_str));
+ if (rc == -1) {
+ MSG("Cookies: Could not write initial string to file %s: %s\n",
+ filename, dStrerror(errno));
+ }
+ }
close(fd);
MSG("Cookies: Created file: %s\n", filename);
@@ -134,10 +136,11 @@ void a_Cookies_freeall()
/*
* Set the value corresponding to the cookie string
*/
-void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
+void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url,
+ const char *date)
{
CookieControlAction action;
- char *cmd, *cookie_string, *dpip_tag, numstr[16];
+ char *cmd, *cookie_string, *dpip_tag;
const char *path;
int i;
@@ -152,10 +155,14 @@ void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
for (i = 0; (cookie_string = dList_nth_data(cookie_strings, i)); ++i) {
path = URL_PATH_(set_url);
- snprintf(numstr, 16, "%d", URL_PORT(set_url));
- cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s port=%s",
- "set_cookie", cookie_string, URL_HOST_(set_url),
- path ? path : "/", numstr);
+ if (date)
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
+ "set_cookie", cookie_string,
+ URL_HOST_(set_url), path ? path : "/", date);
+ else
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
+ "set_cookie", cookie_string,
+ URL_HOST_(set_url), path ? path : "/");
_MSG("Cookies.c: a_Cookies_set \n\t \"%s\" \n",cmd );
/* This call is commented because it doesn't guarantee the order
@@ -174,7 +181,7 @@ void a_Cookies_set(Dlist *cookie_strings, const DilloUrl *set_url)
*/
char *a_Cookies_get_query(const DilloUrl *request_url)
{
- char *cmd, *dpip_tag, *query, numstr[16];
+ char *cmd, *dpip_tag, *query;
const char *path;
CookieControlAction action;
@@ -188,10 +195,9 @@ char *a_Cookies_get_query(const DilloUrl *request_url)
}
path = URL_PATH_(request_url);
- snprintf(numstr, 16, "%d", URL_PORT(request_url));
- cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s port=%s",
+ cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
"get_cookie", URL_SCHEME(request_url),
- URL_HOST(request_url), path ? path : "/", numstr);
+ URL_HOST(request_url), path ? path : "/");
/* Get the answer from cookies.dpi */
_MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
@@ -199,13 +205,11 @@ char *a_Cookies_get_query(const DilloUrl *request_url)
_MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
dFree(cmd);
- query = dStrdup("Cookie2: $Version=\"1\"\r\n");
-
if (dpip_tag != NULL) {
- char *cookie = a_Dpip_get_attr(dpip_tag, strlen(dpip_tag), "cookie");
+ query = a_Dpip_get_attr(dpip_tag, "cookie");
dFree(dpip_tag);
- query = dStrconcat(query, cookie, NULL);
- dFree(cookie);
+ } else {
+ query = dStrdup("");
}
return query;
}
@@ -226,11 +230,10 @@ static int Cookie_control_init(void)
{
CookieControl cc;
FILE *stream;
- char *filename;
+ char *filename, *rc;
char line[LINE_MAXLEN];
char domain[LINE_MAXLEN];
char rule[LINE_MAXLEN];
- int i, j;
bool_t enabled = FALSE;
/* Get a file pointer */
@@ -244,28 +247,31 @@ static int Cookie_control_init(void)
/* Get all lines in the file */
while (!feof(stream)) {
line[0] = '\0';
- fgets(line, LINE_MAXLEN, stream);
+ rc = fgets(line, LINE_MAXLEN, stream);
+ if (!rc && ferror(stream)) {
+ MSG("Cookies1: Error while reading rule from cookiesrc: %s\n",
+ dStrerror(errno));
+ return 2; /* bail out */
+ }
/* Remove leading and trailing whitespaces */
dStrstrip(line);
if (line[0] != '\0' && line[0] != '#') {
- i = 0;
- j = 0;
+ int i = 0, j = 0;
/* Get the domain */
- while (!isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
domain[j++] = line[i++];
domain[j] = '\0';
/* Skip past whitespaces */
- i++;
- while (isspace(line[i]))
+ while (dIsspace(line[i]))
i++;
/* Get the rule */
j = 0;
- while (line[i] != '\0' && !isspace(line[i]))
+ while (line[i] != '\0' && !dIsspace(line[i]))
rule[j++] = line[i++];
rule[j] = '\0';
@@ -287,8 +293,17 @@ static int Cookie_control_init(void)
default_action = cc.action;
dFree(cc.domain);
} else {
+ int i;
+ uint_t len = strlen(cc.domain);
+
+ /* Insert into list such that longest rules come first. */
a_List_add(ccontrol, num_ccontrol, num_ccontrol_max);
- ccontrol[num_ccontrol++] = cc;
+ for (i = num_ccontrol++;
+ i > 0 && (len > strlen(ccontrol[i-1].domain));
+ i--) {
+ ccontrol[i] = ccontrol[i-1];
+ }
+ ccontrol[i] = cc;
}
if (cc.action != COOKIE_DENY)
@@ -302,7 +317,9 @@ static int Cookie_control_init(void)
}
/*
- * Check the rules for an appropriate action for this domain
+ * Check the rules for an appropriate action for this domain.
+ * The rules are ordered by domain length, with longest first, so the
+ * first match is the most specific.
*/
static CookieControlAction Cookies_control_check_domain(const char *domain)
{
diff --git a/src/cookies.h b/src/cookies.h
index 6f9f77e0..482aa5ae 100644
--- a/src/cookies.h
+++ b/src/cookies.h
@@ -12,7 +12,8 @@ extern "C" {
# define a_Cookies_freeall() ;
#else
char *a_Cookies_get_query(const DilloUrl *request_url);
- void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url);
+ void a_Cookies_set(Dlist *cookie_string, const DilloUrl *set_url,
+ const char *server_date);
void a_Cookies_init( void );
void a_Cookies_freeall( void );
#endif
diff --git a/src/css.cc b/src/css.cc
new file mode 100644
index 00000000..988d0dc6
--- /dev/null
+++ b/src/css.cc
@@ -0,0 +1,617 @@
+/*
+ * File: css.cc
+ *
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include "../dlib/dlib.h"
+#include "misc.h"
+#include "msg.h"
+#include "html_common.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+using namespace dw::core::style;
+
+void CssProperty::print () {
+ fprintf (stderr, "%s - %d\n",
+ CssParser::propertyNameString((CssPropertyName)name),
+ (int)value.intVal);
+}
+
+CssPropertyList::~CssPropertyList () {
+ if (ownerOfStrings)
+ for (int i = 0; i < size (); i++)
+ getRef (i)->free ();
+}
+
+/**
+ * \brief Set property to a given name and type.
+ */
+void CssPropertyList::set (CssPropertyName name, CssValueType type,
+ CssPropertyValue value) {
+ CssProperty *prop;
+
+ for (int i = 0; i < size (); i++) {
+ prop = getRef (i);
+
+ if (prop->name == name) {
+ if (ownerOfStrings)
+ prop->free ();
+ prop->type = type;
+ prop->value = value;
+ return;
+ }
+ }
+
+ increase ();
+ prop = getRef (size () - 1);
+ prop->name = name;
+ prop->type = type;
+ prop->value = value;
+}
+
+/**
+ * \brief Merge properties into argument property list.
+ */
+void CssPropertyList::apply (CssPropertyList *props) {
+ for (int i = 0; i < size (); i++)
+ props->set ((CssPropertyName) getRef (i)->name,
+ (CssValueType) getRef (i)->type,
+ getRef (i)->value);
+}
+
+void CssPropertyList::print () {
+ for (int i = 0; i < size (); i++)
+ getRef (i)->print ();
+}
+
+CssSelector::CssSelector () {
+ struct CombinatorAndSelector *cs;
+
+ refCount = 0;
+ selectorList = new lout::misc::SimpleVector
+ <struct CombinatorAndSelector> (1);
+ selectorList->increase ();
+ cs = selectorList->getRef (selectorList->size () - 1);
+
+ cs->notMatchingBefore = -1;
+ cs->selector = new CssSimpleSelector ();
+};
+
+CssSelector::~CssSelector () {
+ for (int i = selectorList->size () - 1; i >= 0; i--)
+ delete selectorList->getRef (i)->selector;
+ delete selectorList;
+}
+
+/**
+ * \brief Return whether selector matches at a given node in the document tree.
+ */
+bool CssSelector::match (Doctree *docTree, const DoctreeNode *node) {
+ CssSimpleSelector *sel;
+ Combinator comb = CHILD;
+ int *notMatchingBefore;
+ const DoctreeNode *n;
+
+ for (int i = selectorList->size () - 1; i >= 0; i--) {
+ struct CombinatorAndSelector *cs = selectorList->getRef (i);
+
+ sel = cs->selector;
+ notMatchingBefore = &cs->notMatchingBefore;
+
+ if (node == NULL)
+ return false;
+
+ switch (comb) {
+ case CHILD:
+ if (!sel->match (node))
+ return false;
+ break;
+ case DESCENDANT:
+ n = node;
+
+ while (true) {
+ if (node == NULL || node->num <= *notMatchingBefore) {
+ *notMatchingBefore = n->num;
+ return false;
+ }
+
+ if (sel->match (node))
+ break;
+
+ node = docTree->parent (node);
+ }
+ break;
+ default:
+ return false; // \todo implement other combinators
+ }
+
+ comb = cs->combinator;
+ node = docTree->parent (node);
+ }
+
+ return true;
+}
+
+void CssSelector::addSimpleSelector (Combinator c) {
+ struct CombinatorAndSelector *cs;
+
+ selectorList->increase ();
+ cs = selectorList->getRef (selectorList->size () - 1);
+
+ cs->combinator = c;
+ cs->notMatchingBefore = -1;
+ cs->selector = new CssSimpleSelector ();
+}
+
+/**
+ * \brief Return the specificity of the selector.
+ *
+ * The specificity of a CSS selector is defined in
+ * http://www.w3.org/TR/CSS21/cascade.html#specificity
+ */
+int CssSelector::specificity () {
+ int spec = 0;
+
+ for (int i = 0; i < selectorList->size (); i++)
+ spec += selectorList->getRef (i)->selector->specificity ();
+
+ return spec;
+}
+
+void CssSelector::print () {
+ for (int i = 0; i < selectorList->size (); i++) {
+ selectorList->getRef (i)->selector->print ();
+
+ if (i < selectorList->size () - 1) {
+ switch (selectorList->getRef (i + 1)->combinator) {
+ case CHILD:
+ fprintf (stderr, "> ");
+ break;
+ case DESCENDANT:
+ fprintf (stderr, "\" \" ");
+ break;
+ default:
+ fprintf (stderr, "? ");
+ break;
+ }
+ }
+ }
+
+ fprintf (stderr, "\n");
+}
+
+CssSimpleSelector::CssSimpleSelector () {
+ element = ELEMENT_ANY;
+ klass = NULL;
+ id = NULL;
+ pseudo = NULL;
+}
+
+CssSimpleSelector::~CssSimpleSelector () {
+ if (klass) {
+ for (int i = 0; i < klass->size (); i++)
+ dFree (klass->get (i));
+ delete klass;
+ }
+ dFree (id);
+ dFree (pseudo);
+}
+
+void CssSimpleSelector::setSelect (SelectType t, const char *v) {
+ switch (t) {
+ case SELECT_CLASS:
+ if (klass == NULL)
+ klass = new lout::misc::SimpleVector <char *> (1);
+ klass->increase ();
+ klass->set (klass->size () - 1, dStrdup (v));
+ break;
+ case SELECT_PSEUDO_CLASS:
+ if (pseudo == NULL)
+ pseudo = dStrdup (v);
+ break;
+ case SELECT_ID:
+ if (id == NULL)
+ id = dStrdup (v);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * \brief Return whether simple selector matches at a given node of
+ * the document tree.
+ */
+bool CssSimpleSelector::match (const DoctreeNode *n) {
+ if (element != ELEMENT_ANY && element != n->element)
+ return false;
+ if (pseudo != NULL &&
+ (n->pseudo == NULL || dStrcasecmp (pseudo, n->pseudo) != 0))
+ return false;
+ if (id != NULL && (n->id == NULL || dStrcasecmp (id, n->id) != 0))
+ return false;
+ if (klass != NULL) {
+ for (int i = 0; i < klass->size (); i++) {
+ bool found = false;
+ if (n->klass != NULL) {
+ for (int j = 0; j < n->klass->size (); j++) {
+ if (dStrcasecmp (klass->get(i), n->klass->get(j)) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (! found)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * \brief Return the specificity of the simple selector.
+ *
+ * The result is used in CssSelector::specificity ().
+ */
+int CssSimpleSelector::specificity () {
+ int spec = 0;
+
+ if (id)
+ spec += 1 << 20;
+ if (klass)
+ spec += klass->size() << 10;
+ if (pseudo)
+ spec += 1 << 10;
+ if (element != ELEMENT_ANY)
+ spec += 1;
+
+ return spec;
+}
+
+void CssSimpleSelector::print () {
+ fprintf (stderr, "Element %d, pseudo %s, id %s ",
+ element, pseudo, id);
+ if (klass != NULL) {
+ fprintf (stderr, "class ");
+ for (int i = 0; i < klass->size (); i++)
+ fprintf (stderr, ".%s", klass->get (i));
+ }
+}
+
+CssRule::CssRule (CssSelector *selector, CssPropertyList *props, int pos) {
+ assert (selector->size () > 0);
+
+ this->selector = selector;
+ this->selector->ref ();
+ this->props = props;
+ this->props->ref ();
+ this->pos = pos;
+ spec = selector->specificity ();
+};
+
+CssRule::~CssRule () {
+ selector->unref ();
+ props->unref ();
+};
+
+void CssRule::apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node) {
+ if (selector->match (docTree, node))
+ this->props->apply (props);
+}
+
+void CssRule::print () {
+ selector->print ();
+ props->print ();
+}
+
+/*
+ * \brief Insert rule with increasing specificity.
+ *
+ * If two rules have the same specificity, the one that was added later
+ * will be added behind the others.
+ * This gives later added rules more weight.
+ */
+void CssStyleSheet::RuleList::insert (CssRule *rule) {
+ increase ();
+ int i = size () - 1;
+
+ while (i > 0 && rule->specificity () < get (i - 1)->specificity ()) {
+ *getRef (i) = get (i - 1);
+ i--;
+ }
+
+ *getRef (i) = rule;
+}
+
+CssStyleSheet::CssStyleSheet () {
+ for (int i = 0; i < ntags; i++)
+ elementTable[i] = new RuleList ();
+
+ idTable = new RuleMap ();
+ classTable = new RuleMap ();
+ anyTable = new RuleList ();
+}
+
+CssStyleSheet::~CssStyleSheet () {
+ for (int i = 0; i < ntags; i++)
+ delete elementTable[i];
+ delete idTable;
+ delete classTable;
+ delete anyTable;
+}
+
+/**
+ * \brief Insert a rule into CssStyleSheet.
+ *
+ * To improve matching performance the rules are organized into
+ * rule lists based on the topmost simple selector of their selector.
+ */
+void CssStyleSheet::addRule (CssRule *rule) {
+ CssSimpleSelector *top = rule->selector->top ();
+ RuleList *ruleList = NULL;
+ lout::object::ConstString *string;
+
+ if (top->getId ()) {
+ string = new lout::object::ConstString (top->getId ());
+ ruleList = idTable->get (string);
+ if (ruleList == NULL) {
+ ruleList = new RuleList ();
+ idTable->put (string, ruleList);
+ } else {
+ delete string;
+ }
+ } else if (top->getClass () && top->getClass ()->size () > 0) {
+ string = new lout::object::ConstString (top->getClass ()->get (0));
+ ruleList = classTable->get (string);
+ if (ruleList == NULL) {
+ ruleList = new RuleList;
+ classTable->put (string, ruleList);
+ } else {
+ delete string;
+ }
+ } else if (top->getElement () >= 0 && top->getElement () < ntags) {
+ ruleList = elementTable[top->getElement ()];
+ } else if (top->getElement () == CssSimpleSelector::ELEMENT_ANY) {
+ ruleList = anyTable;
+ }
+
+ if (ruleList) {
+ ruleList->insert (rule);
+ } else {
+ assert (top->getElement () == CssSimpleSelector::ELEMENT_NONE);
+ delete rule;
+ }
+}
+
+/**
+ * \brief Apply a stylesheet to a property list.
+ *
+ * The properties are set as defined by the rules in the stylesheet that
+ * match at the given node in the document tree.
+ */
+void CssStyleSheet::apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node) {
+ static const int maxLists = 32;
+ RuleList *ruleList[maxLists];
+ int numLists = 0, index[maxLists] = {0};
+
+ if (node->id) {
+ lout::object::ConstString idString (node->id);
+
+ ruleList[numLists] = idTable->get (&idString);
+ if (ruleList[numLists])
+ numLists++;
+ }
+
+ if (node->klass) {
+ for (int i = 0; i < node->klass->size (); i++) {
+ if (i >= maxLists - 4) {
+ MSG_WARN("Maximum number of classes per element exceeded.\n");
+ break;
+ }
+
+ lout::object::ConstString classString (node->klass->get (i));
+
+ ruleList[numLists] = classTable->get (&classString);
+ if (ruleList[numLists])
+ numLists++;
+ }
+ }
+
+ ruleList[numLists] = elementTable[docTree->top ()->element];
+ if (ruleList[numLists])
+ numLists++;
+
+ ruleList[numLists] = anyTable;
+ if (ruleList[numLists])
+ numLists++;
+
+ // Apply potentially matching rules from ruleList[0-numLists] with
+ // ascending specificity.
+ // If specificity is equal, rules are applied in order of appearance.
+ // Each ruleList is sorted already.
+ while (true) {
+ int minSpec = 1 << 30;
+ int minPos = 1 << 30;
+ int minSpecIndex = -1;
+
+ for (int i = 0; i < numLists; i++) {
+ if (ruleList[i] && ruleList[i]->size () > index[i] &&
+ (ruleList[i]->get(index[i])->specificity () < minSpec ||
+ (ruleList[i]->get(index[i])->specificity () == minSpec &&
+ ruleList[i]->get(index[i])->position () < minPos))) {
+
+ minSpec = ruleList[i]->get(index[i])->specificity ();
+ minPos = ruleList[i]->get(index[i])->position ();
+ minSpecIndex = i;
+ }
+ }
+
+ if (minSpecIndex >= 0) {
+ ruleList[minSpecIndex]->get (index[minSpecIndex])->apply
+ (props, docTree, node);
+ index[minSpecIndex]++;
+ } else {
+ break;
+ }
+ }
+}
+
+CssStyleSheet *CssContext::userAgentStyle;
+CssStyleSheet *CssContext::userStyle;
+CssStyleSheet *CssContext::userImportantStyle;
+
+CssContext::CssContext () {
+ pos = 0;
+
+ for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
+ sheet[o] = NULL;
+
+ if (userAgentStyle == NULL) {
+ userAgentStyle = new CssStyleSheet ();
+ userStyle = new CssStyleSheet ();
+ userImportantStyle = new CssStyleSheet ();
+
+ sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
+ sheet[CSS_PRIMARY_USER] = userStyle;
+ sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
+
+ buildUserAgentStyle ();
+ buildUserStyle ();
+ }
+
+ sheet[CSS_PRIMARY_USER_AGENT] = userAgentStyle;
+ sheet[CSS_PRIMARY_USER] = userStyle;
+ sheet[CSS_PRIMARY_USER_IMPORTANT] = userImportantStyle;
+}
+
+CssContext::~CssContext () {
+ for (int o = CSS_PRIMARY_USER_AGENT; o < CSS_PRIMARY_LAST; o++)
+ if (sheet[o] != userAgentStyle && sheet[o] != userStyle &&
+ sheet[o] != userImportantStyle)
+ delete sheet[o];
+}
+
+/**
+ * \brief Apply a CSS context to a property list.
+ *
+ * The stylesheets in the context are applied one after the other
+ * in the ordering defined by CSS 2.1.
+ * Stylesheets that are applied later can overwrite properties set
+ * by previous stylesheets.
+ * This allows e.g. user styles to overwrite author styles.
+ */
+void CssContext::apply (CssPropertyList *props, Doctree *docTree,
+ CssPropertyList *tagStyle, CssPropertyList *nonCssHints) {
+ const DoctreeNode *node = docTree->top ();
+
+ if (sheet[CSS_PRIMARY_USER_AGENT])
+ sheet[CSS_PRIMARY_USER_AGENT]->apply (props, docTree, node);
+
+ if (sheet[CSS_PRIMARY_USER])
+ sheet[CSS_PRIMARY_USER]->apply (props, docTree, node);
+
+ if (nonCssHints)
+ nonCssHints->apply (props);
+
+ if (sheet[CSS_PRIMARY_AUTHOR])
+ sheet[CSS_PRIMARY_AUTHOR]->apply (props, docTree, node);
+
+ if (tagStyle)
+ tagStyle->apply (props);
+
+ if (sheet[CSS_PRIMARY_AUTHOR_IMPORTANT])
+ sheet[CSS_PRIMARY_AUTHOR_IMPORTANT]->apply (props, docTree, node);
+
+ if (sheet[CSS_PRIMARY_USER_IMPORTANT])
+ sheet[CSS_PRIMARY_USER_IMPORTANT]->apply (props, docTree, node);
+}
+
+void CssContext::addRule (CssSelector *sel, CssPropertyList *props,
+ CssPrimaryOrder order) {
+
+ if (props->size () > 0) {
+ CssRule *rule = new CssRule (sel, props, pos++);
+
+ if (sheet[order] == NULL)
+ sheet[order] = new CssStyleSheet ();
+
+ sheet[order]->addRule (rule);
+ }
+}
+
+/**
+ * \brief Create the user agent style.
+ *
+ * The user agent style defines how dillo renders HTML in the absence of
+ * author or user styles.
+ */
+void CssContext::buildUserAgentStyle () {
+ const char *cssBuf =
+ "body {background-color: #e0e0a3; font-family: sans-serif; color: black;"
+ " margin: 5px}"
+ "big {font-size: 1.17em}"
+ "blockquote, dd {margin-left: 40px; margin-right: 40px}"
+ "center {text-align: center}"
+ "dt {font-weight: bolder}"
+ ":link {color: blue; text-decoration: underline; cursor: pointer}"
+ ":visited {color: #800080; text-decoration: underline; cursor: pointer}"
+ "h1, h2, h3, h4, h5, h6, b, strong {font-weight: bolder}"
+ "i, em, cite, address, var {font-style: italic}"
+ ":link img, :visited img {border: 1px solid}"
+ "frameset, ul, ol, dir {margin-left: 40px}"
+ "h1 {font-size: 2em; margin-top: .67em; margin-bottom: 0}"
+ "h2 {font-size: 1.5em; margin-top: .75em; margin-bottom: 0}"
+ "h3 {font-size: 1.17em; margin-top: .83em; margin-bottom: 0}"
+ "h4 {margin-top: 1.12em; margin-bottom: 0}"
+ "h5 {font-size: 0.83em; margin-top: 1.5em; margin-bottom: 0}"
+ "h6 {font-size: 0.75em; margin-top: 1.67em; margin-bottom: 0}"
+ "hr {width: 100%; border: 1px inset}"
+ "li {margin-top: 0.1em}"
+ "pre {white-space: pre}"
+ "ol {list-style-type: decimal}"
+ "ul {list-style-type: disc}"
+ "ul ul {list-style-type: circle}"
+ "ul ul ul {list-style-type: square}"
+ "ul ul ul ul {list-style-type: disc}"
+ "u {text-decoration: underline}"
+ "small, sub, sup {font-size: 0.83em}"
+ "sub {vertical-align: sub}"
+ "sup {vertical-align: super}"
+ "s, strike, del {text-decoration: line-through}"
+ "table {border-style: outset; border-spacing: 1px}"
+ "td, th {border-style: inset; padding: 2px}"
+ "thead, tbody, tfoot {vertical-align: middle}"
+ "th {font-weight: bolder; text-align: center}"
+ "code, tt, pre, samp, kbd {font-family: monospace}"
+ /* WORKAROUND: Reset font properties in tables as some
+ * some pages rely on it (e.g. gmail).
+ * http://developer.mozilla.org/En/Fixing_Table_Inheritance_in_Quirks_Mode
+ * has a detailed description of the issue.
+ */
+ "table, caption {font-size: medium; font-weight: normal}";
+
+ CssParser::parse (NULL, NULL, this, cssBuf, strlen (cssBuf),
+ CSS_ORIGIN_USER_AGENT);
+}
+
+void CssContext::buildUserStyle () {
+ Dstr *style;
+ char *filename = dStrconcat(dGethomedir(), "/.dillo/style.css", NULL);
+
+ if ((style = a_Misc_file2dstr(filename))) {
+ CssParser::parse (NULL,NULL,this,style->str, style->len,CSS_ORIGIN_USER);
+ dStr_free (style, 1);
+ }
+ dFree (filename);
+}
diff --git a/src/css.hh b/src/css.hh
new file mode 100644
index 00000000..b23eb9a3
--- /dev/null
+++ b/src/css.hh
@@ -0,0 +1,495 @@
+#ifndef __CSS_HH__
+#define __CSS_HH__
+
+#include "dw/core.hh"
+#include "doctree.hh"
+
+/* Origin and weight. Used only internally.*/
+typedef enum {
+ CSS_PRIMARY_USER_AGENT,
+ CSS_PRIMARY_USER,
+ CSS_PRIMARY_AUTHOR,
+ CSS_PRIMARY_AUTHOR_IMPORTANT,
+ CSS_PRIMARY_USER_IMPORTANT,
+ CSS_PRIMARY_LAST,
+} CssPrimaryOrder;
+
+typedef enum {
+ CSS_ORIGIN_USER_AGENT,
+ CSS_ORIGIN_USER,
+ CSS_ORIGIN_AUTHOR,
+} CssOrigin;
+
+typedef enum {
+ CSS_TYPE_INTEGER, /* This type is only used internally, for x-*
+ properties. */
+ CSS_TYPE_ENUM, /* Value is i, if represented by
+ enum_symbols[i]. */
+ CSS_TYPE_MULTI_ENUM, /* For all enum_symbols[i], 1 << i are
+ combined. */
+ CSS_TYPE_LENGTH_PERCENTAGE, /* <length> or <percentage>. Represented by
+ CssLength. */
+ CSS_TYPE_LENGTH, /* <length>, represented as CssLength.
+ Note: In some cases, CSS_TYPE_LENGTH is used
+ instead of CSS_TYPE_LENGTH_PERCENTAGE,
+ only because Dw cannot handle percentages
+ in this particular case (e.g.
+ 'margin-*-width'). */
+ CSS_TYPE_SIGNED_LENGTH, /* As CSS_TYPE_LENGTH but may be negative. */
+ CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, /* <length> or <percentage>, or <number> */
+ CSS_TYPE_COLOR, /* Represented as integer. */
+ CSS_TYPE_FONT_WEIGHT, /* this very special and only used by
+ 'font-weight' */
+ CSS_TYPE_STRING, /* <string> */
+ CSS_TYPE_SYMBOL, /* Symbols, which are directly copied (as
+ opposed to CSS_TYPE_ENUM and
+ CSS_TYPE_MULTI_ENUM). Used for
+ 'font-family'. */
+ CSS_TYPE_UNUSED /* Not yet used. Will itself get unused some
+ day. */
+} CssValueType;
+
+/*
+ * Lengths are represented as int in the following way:
+ *
+ * | <------ integer value ------> |
+ *
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | integer part | type |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * | integer part | decimal fraction | type |
+ * +---+ - - - +---+---+- - - - - -+---+---+---+---+
+ * n-1 15 14 3 2 1 0
+ *
+ * | <------ fixed point value ------> |
+ *
+ * where type is one of the CSS_LENGTH_TYPE_* values.
+ * CSS_LENGTH_TYPE_PX values are stored as
+ * 29 bit signed integer, all other types as fixed point values.
+ */
+
+typedef int CssLength;
+
+typedef enum {
+ CSS_LENGTH_TYPE_NONE,
+ CSS_LENGTH_TYPE_PX,
+ CSS_LENGTH_TYPE_MM, /* "cm", "in", "pt" and "pc" are converted into
+ millimeters. */
+ CSS_LENGTH_TYPE_EM,
+ CSS_LENGTH_TYPE_EX,
+ CSS_LENGTH_TYPE_PERCENTAGE,
+ CSS_LENGTH_TYPE_RELATIVE, /* This does not exist in CSS but
+ is used in HTML */
+ CSS_LENGTH_TYPE_AUTO /* This can be used as a simple value. */
+} CssLengthType;
+
+inline CssLength CSS_CREATE_LENGTH (float v, CssLengthType t) {
+ static const int CSS_LENGTH_FRAC_MAX = (1 << (32 - 15 - 1)) - 1;
+ static const int CSS_LENGTH_INT_MAX = (1 << (32 - 4)) - 1;
+ int iv;
+
+ switch (t) {
+ case CSS_LENGTH_TYPE_PX:
+ iv = (int) (v + 0.5);
+ if (iv > CSS_LENGTH_INT_MAX)
+ iv = CSS_LENGTH_INT_MAX;
+ else if (iv < -CSS_LENGTH_INT_MAX)
+ iv = -CSS_LENGTH_INT_MAX;
+ return iv << 3 | t;
+ case CSS_LENGTH_TYPE_NONE:
+ case CSS_LENGTH_TYPE_MM:
+ case CSS_LENGTH_TYPE_EM:
+ case CSS_LENGTH_TYPE_EX:
+ case CSS_LENGTH_TYPE_PERCENTAGE:
+ case CSS_LENGTH_TYPE_RELATIVE:
+ if (v > CSS_LENGTH_FRAC_MAX)
+ v = CSS_LENGTH_FRAC_MAX;
+ else if (v < -CSS_LENGTH_FRAC_MAX)
+ v = -CSS_LENGTH_FRAC_MAX;
+ return ((int) (v * (1 << 15)) & ~7 ) | t;
+ case CSS_LENGTH_TYPE_AUTO:
+ return t;
+ default:
+ assert(false);
+ return CSS_LENGTH_TYPE_AUTO;
+ }
+}
+
+inline CssLengthType CSS_LENGTH_TYPE (CssLength l) {
+ return (CssLengthType) (l & 7);
+}
+
+inline float CSS_LENGTH_VALUE (CssLength l) {
+ switch (CSS_LENGTH_TYPE(l)) {
+ case CSS_LENGTH_TYPE_PX:
+ return (float) (l >> 3);
+ case CSS_LENGTH_TYPE_NONE:
+ case CSS_LENGTH_TYPE_MM:
+ case CSS_LENGTH_TYPE_EM:
+ case CSS_LENGTH_TYPE_EX:
+ case CSS_LENGTH_TYPE_PERCENTAGE:
+ case CSS_LENGTH_TYPE_RELATIVE:
+ return ((float)(l & ~7)) / (1 << 15);
+ case CSS_LENGTH_TYPE_AUTO:
+ return 0.0;
+ default:
+ assert(false);
+ return 0.0;
+ }
+}
+
+typedef enum {
+ CSS_PROPERTY_BACKGROUND_ATTACHMENT,
+ CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_PROPERTY_BACKGROUND_IMAGE,
+ CSS_PROPERTY_BACKGROUND_POSITION,
+ CSS_PROPERTY_BACKGROUND_REPEAT,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_COLLAPSE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_SPACING,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BOTTOM,
+ CSS_PROPERTY_CAPTION_SIDE,
+ CSS_PROPERTY_CLEAR,
+ CSS_PROPERTY_CLIP,
+ CSS_PROPERTY_COLOR,
+ CSS_PROPERTY_CONTENT,
+ CSS_PROPERTY_COUNTER_INCREMENT,
+ CSS_PROPERTY_COUNTER_RESET,
+ CSS_PROPERTY_CURSOR,
+ CSS_PROPERTY_DIRECTION,
+ CSS_PROPERTY_DISPLAY,
+ CSS_PROPERTY_EMPTY_CELLS,
+ CSS_PROPERTY_FLOAT,
+ CSS_PROPERTY_FONT_FAMILY,
+ CSS_PROPERTY_FONT_SIZE,
+ CSS_PROPERTY_FONT_SIZE_ADJUST,
+ CSS_PROPERTY_FONT_STRETCH,
+ CSS_PROPERTY_FONT_STYLE,
+ CSS_PROPERTY_FONT_VARIANT,
+ CSS_PROPERTY_FONT_WEIGHT,
+ CSS_PROPERTY_HEIGHT,
+ CSS_PROPERTY_LEFT,
+ CSS_PROPERTY_LETTER_SPACING,
+ CSS_PROPERTY_LINE_HEIGHT,
+ CSS_PROPERTY_LIST_STYLE_IMAGE,
+ CSS_PROPERTY_LIST_STYLE_POSITION,
+ CSS_PROPERTY_LIST_STYLE_TYPE,
+ CSS_PROPERTY_MARGIN_BOTTOM,
+ CSS_PROPERTY_MARGIN_LEFT,
+ CSS_PROPERTY_MARGIN_RIGHT,
+ CSS_PROPERTY_MARGIN_TOP,
+ CSS_PROPERTY_MARKER_OFFSET,
+ CSS_PROPERTY_MARKS,
+ CSS_PROPERTY_MAX_HEIGHT,
+ CSS_PROPERTY_MAX_WIDTH,
+ CSS_PROPERTY_MIN_HEIGHT,
+ CSS_PROPERTY_MIN_WIDTH,
+ CSS_PROPERTY_OUTLINE_COLOR,
+ CSS_PROPERTY_OUTLINE_STYLE,
+ CSS_PROPERTY_OUTLINE_WIDTH,
+ CSS_PROPERTY_OVERFLOW,
+ CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_PROPERTY_PADDING_LEFT,
+ CSS_PROPERTY_PADDING_RIGHT,
+ CSS_PROPERTY_PADDING_TOP,
+ CSS_PROPERTY_POSITION,
+ CSS_PROPERTY_QUOTES,
+ CSS_PROPERTY_RIGHT,
+ CSS_PROPERTY_TEXT_ALIGN,
+ CSS_PROPERTY_TEXT_DECORATION,
+ CSS_PROPERTY_TEXT_INDENT,
+ CSS_PROPERTY_TEXT_SHADOW,
+ CSS_PROPERTY_TEXT_TRANSFORM,
+ CSS_PROPERTY_TOP,
+ CSS_PROPERTY_UNICODE_BIDI,
+ CSS_PROPERTY_VERTICAL_ALIGN,
+ CSS_PROPERTY_VISIBILITY,
+ CSS_PROPERTY_WHITE_SPACE,
+ CSS_PROPERTY_WIDTH,
+ CSS_PROPERTY_WORD_SPACING,
+ CSS_PROPERTY_Z_INDEX,
+ CSS_PROPERTY_X_LINK,
+ CSS_PROPERTY_X_COLSPAN,
+ CSS_PROPERTY_X_ROWSPAN,
+ PROPERTY_X_LINK,
+ PROPERTY_X_IMG,
+ PROPERTY_X_TOOLTIP,
+ CSS_PROPERTY_LAST
+} CssPropertyName;
+
+typedef union {
+ int32_t intVal;
+ char *strVal;
+} CssPropertyValue;
+
+typedef enum {
+ CSS_BORDER_WIDTH_THIN,
+ CSS_BORDER_WIDTH_MEDIUM,
+ CSS_BORDER_WIDTH_THICK,
+} CssBorderWidthExtensions;
+
+typedef enum {
+ CSS_FONT_WEIGHT_BOLD,
+ CSS_FONT_WEIGHT_BOLDER,
+ CSS_FONT_WEIGHT_LIGHT,
+ CSS_FONT_WEIGHT_LIGHTER,
+ CSS_FONT_WEIGHT_NORMAL,
+} CssFontWeightExtensions;
+
+typedef enum {
+ CSS_FONT_SIZE_LARGE,
+ CSS_FONT_SIZE_LARGER,
+ CSS_FONT_SIZE_MEDIUM,
+ CSS_FONT_SIZE_SMALL,
+ CSS_FONT_SIZE_SMALLER,
+ CSS_FONT_SIZE_XX_LARGE,
+ CSS_FONT_SIZE_XX_SMALL,
+ CSS_FONT_SIZE_X_LARGE,
+ CSS_FONT_SIZE_X_SMALL,
+} CssFontSizeExtensions;
+
+typedef enum {
+ CSS_LETTER_SPACING_NORMAL
+} CssLetterSpacingExtensions;
+
+typedef enum {
+ CSS_WORD_SPACING_NORMAL
+} CssWordSpacingExtensions;
+
+
+/**
+ * \brief This class holds a CSS property and value pair.
+ */
+class CssProperty {
+ public:
+
+ short name;
+ short type;
+ CssPropertyValue value;
+
+ inline void free () {
+ switch (type) {
+ case CSS_TYPE_STRING:
+ case CSS_TYPE_SYMBOL:
+ dFree (value.strVal);
+ break;
+ default:
+ break;
+ }
+ }
+ void print ();
+};
+
+/**
+ * \brief A list of CssProperty objects.
+ */
+class CssPropertyList : public lout::misc::SimpleVector <CssProperty> {
+ int refCount;
+ bool ownerOfStrings;
+
+ public:
+ inline CssPropertyList(bool ownerOfStrings = false) :
+ lout::misc::SimpleVector <CssProperty> (1) {
+ refCount = 0;
+ this->ownerOfStrings = ownerOfStrings;
+ };
+ inline CssPropertyList(const CssPropertyList &p) :
+ lout::misc::SimpleVector <CssProperty> (p) {
+ refCount = 0;
+ ownerOfStrings = false;
+ };
+ ~CssPropertyList ();
+
+ void set (CssPropertyName name, CssValueType type,
+ CssPropertyValue value);
+ inline void set (CssPropertyName name, CssValueType type, char *value) {
+ CssPropertyValue v;
+ v.strVal = value;
+ set (name, type, v);
+ };
+ inline void set (CssPropertyName name, CssValueType type, int value) {
+ CssPropertyValue v;
+ v.intVal = value;
+ set (name, type, v);
+ };
+ void apply (CssPropertyList *props);
+ void print ();
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+};
+
+class CssSimpleSelector {
+ private:
+ int element;
+ char *pseudo, *id;
+ lout::misc::SimpleVector <char *> *klass;
+
+ public:
+ enum {
+ ELEMENT_NONE = -1,
+ ELEMENT_ANY = -2,
+ };
+
+ typedef enum {
+ SELECT_NONE,
+ SELECT_CLASS,
+ SELECT_PSEUDO_CLASS,
+ SELECT_ID,
+ } SelectType;
+
+ CssSimpleSelector ();
+ ~CssSimpleSelector ();
+ inline void setElement (int e) { element = e; };
+ void setSelect (SelectType t, const char *v);
+ inline lout::misc::SimpleVector <char *> *getClass () { return klass; };
+ inline const char *getPseudoClass () { return pseudo; };
+ inline const char *getId () { return id; };
+ inline int getElement () { return element; };
+ bool match (const DoctreeNode *node);
+ int specificity ();
+ void print ();
+};
+
+/**
+ * \brief CSS selector class.
+ *
+ * \todo Implement missing selector options.
+ */
+class CssSelector {
+ public:
+ typedef enum {
+ DESCENDANT,
+ CHILD,
+ ADJACENT_SIBLING,
+ } Combinator;
+
+ private:
+ struct CombinatorAndSelector {
+ int notMatchingBefore; // used for optimizing CSS selector matching
+ Combinator combinator;
+ CssSimpleSelector *selector;
+ };
+
+ int refCount;
+ lout::misc::SimpleVector <struct CombinatorAndSelector> *selectorList;
+
+ public:
+ CssSelector ();
+ ~CssSelector ();
+ void addSimpleSelector (Combinator c);
+ inline CssSimpleSelector *top () {
+ return selectorList->getRef (selectorList->size () - 1)->selector;
+ };
+ inline int size () { return selectorList->size (); };
+ bool match (Doctree *dt, const DoctreeNode *node);
+ int specificity ();
+ void print ();
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+};
+
+/**
+ * \brief A CssSelector CssPropertyList pair.
+ *
+ * The CssPropertyList is applied if the CssSelector matches.
+ */
+class CssRule {
+ private:
+ CssPropertyList *props;
+ int spec, pos;
+
+ public:
+ CssSelector *selector;
+
+ CssRule (CssSelector *selector, CssPropertyList *props, int pos);
+ ~CssRule ();
+
+ void apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node);
+ inline int specificity () { return spec; };
+ inline int position () { return pos; };
+ void print ();
+};
+
+/**
+ * \brief A list of CssRules.
+ *
+ * In apply () all matching rules are applied.
+ */
+class CssStyleSheet {
+ private:
+ class RuleList : public lout::misc::SimpleVector <CssRule*>,
+ public lout::object::Object {
+ public:
+ RuleList () : lout::misc::SimpleVector <CssRule*> (1) {};
+ ~RuleList () {
+ for (int i = 0; i < size (); i++)
+ delete get (i);
+ };
+
+ void insert (CssRule *rule);
+ inline bool equals (lout::object::Object *other) {
+ return this == other;
+ };
+ inline int hashValue () { return (intptr_t) this; };
+ };
+
+ class RuleMap : public lout::container::typed::HashTable
+ <lout::object::ConstString, RuleList > {
+ public:
+ RuleMap () : lout::container::typed::HashTable
+ <lout::object::ConstString, RuleList > (true, true, 256) {};
+ };
+
+ static const int ntags = 90; // \todo replace 90
+ RuleList *elementTable[ntags];
+
+ RuleMap *idTable;
+ RuleMap *classTable;
+ RuleList *anyTable;
+
+ public:
+ CssStyleSheet();
+ ~CssStyleSheet();
+ void addRule (CssRule *rule);
+ void apply (CssPropertyList *props,
+ Doctree *docTree, const DoctreeNode *node);
+};
+
+/**
+ * \brief A set of CssStyleSheets.
+ */
+class CssContext {
+ private:
+ static CssStyleSheet *userAgentStyle;
+ static CssStyleSheet *userStyle;
+ static CssStyleSheet *userImportantStyle;
+ CssStyleSheet *sheet[CSS_PRIMARY_USER_IMPORTANT + 1];
+ int pos;
+
+ void buildUserAgentStyle ();
+ void buildUserStyle ();
+
+ public:
+ CssContext ();
+ ~CssContext ();
+
+ void addRule (CssSelector *sel, CssPropertyList *props,
+ CssPrimaryOrder order);
+ void apply (CssPropertyList *props,
+ Doctree *docTree,
+ CssPropertyList *tagStyle, CssPropertyList *nonCssHints);
+};
+
+#endif
diff --git a/src/cssparser.cc b/src/cssparser.cc
new file mode 100644
index 00000000..00ba7428
--- /dev/null
+++ b/src/cssparser.cc
@@ -0,0 +1,1476 @@
+/*
+ * File: cssparser.cc
+ *
+ * Copyright 2004 Sebastian Geerken <sgeerken@dillo.org>
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+/*
+ * This file is heavily based on the CSS parser of dillo-0.8.0-css-3 -
+ * a dillo1 based CSS prototype written by Sebastian Geerken.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "msg.h"
+#include "colors.h"
+#include "html_common.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+using namespace dw::core::style;
+
+#define DEBUG_MSG(A, B, ...) _MSG(B, __VA_ARGS__)
+#define MSG_CSS(A, ...) MSG(A, __VA_ARGS__)
+#define DEBUG_TOKEN_LEVEL 0
+#define DEBUG_PARSE_LEVEL 0
+#define DEBUG_CREATE_LEVEL 0
+
+#define DEBUG_LEVEL 10
+
+/* The last three ones are never parsed. */
+#define CSS_NUM_INTERNAL_PROPERTIES 3
+#define CSS_NUM_PARSED_PROPERTIES \
+ (CSS_PROPERTY_LAST - CSS_NUM_INTERNAL_PROPERTIES)
+
+
+typedef struct {
+ const char *symbol;
+ const CssValueType type[3];
+ const char *const *enum_symbols;
+} CssPropertyInfo;
+
+static const char *const Css_border_style_enum_vals[] = {
+ "none", "hidden", "dotted", "dashed", "solid", "double", "groove",
+ "ridge", "inset", "outset", NULL
+};
+
+static const char *const Css_border_width_enum_vals[] = {
+ "thin", "medium", "thick", NULL
+};
+
+static const char *const Css_cursor_enum_vals[] = {
+ "crosshair", "default", "pointer", "move", "e-resize", "ne-resize",
+ "nw-resize", "n-resize", "se-resize", "sw-resize", "s-resize",
+ "w-resize", "text", "wait", "help", NULL
+};
+
+static const char *const Css_display_enum_vals[] = {
+ "block", "inline", "list-item", "none", "table", "table-row-group",
+ "table-header-group", "table-footer-group", "table-row",
+ "table-cell", NULL
+};
+
+static const char *const Css_font_size_enum_vals[] = {
+ "large", "larger", "medium", "small", "smaller", "xx-large", "xx-small",
+ "x-large", "x-small", NULL
+};
+
+static const char *const Css_font_style_enum_vals[] = {
+ "normal", "italic", "oblique", NULL
+};
+
+static const char *const Css_font_weight_enum_vals[] = {
+ "bold", "bolder", "light", "lighter", "normal", NULL
+};
+
+static const char *const Css_letter_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_position_enum_vals[] = {
+ "inside", "outside", NULL
+};
+
+static const char *const Css_line_height_enum_vals[] = {
+ "normal", NULL
+};
+
+static const char *const Css_list_style_type_enum_vals[] = {
+ "disc", "circle", "square", "decimal", "decimal-leading-zero",
+ "lower-roman", "upper-roman", "lower-greek", "lower-alpha",
+ "lower-latin", "upper-alpha", "upper-latin", "hebrew", "armenian",
+ "georgian", "cjk-ideographic", "hiragana", "katakana", "hiragana-iroha",
+ "katakana-iroha", "none", NULL
+};
+
+static const char *const Css_text_align_enum_vals[] = {
+ "left", "right", "center", "justify", "string", NULL
+};
+
+static const char *const Css_text_decoration_enum_vals[] = {
+ "underline", "overline", "line-through", "blink", NULL
+};
+
+static const char *const Css_vertical_align_vals[] = {
+ "top", "bottom", "middle", "baseline", "sub", "super", "text-top",
+ "text-bottom", NULL
+};
+
+static const char *const Css_white_space_vals[] = {
+ "normal", "pre", "nowrap", "pre-wrap", "pre-line", NULL
+};
+
+static const char *const Css_word_spacing_enum_vals[] = {
+ "normal", NULL
+};
+
+const CssPropertyInfo Css_property_info[CSS_PROPERTY_LAST] = {
+ {"background-attachment", {CSS_TYPE_UNUSED}, NULL},
+ {"background-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"background-image", {CSS_TYPE_UNUSED}, NULL},
+ {"background-position", {CSS_TYPE_UNUSED}, NULL},
+ {"background-repeat", {CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-bottom-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-bottom-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-collapse", {CSS_TYPE_UNUSED}, NULL},
+ {"border-left-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-left-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-left-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-right-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-right-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-rigth-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"border-spacing", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"border-top-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_border_style_enum_vals},
+ {"border-top-width", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH, CSS_TYPE_UNUSED},
+ Css_border_width_enum_vals},
+ {"bottom", {CSS_TYPE_UNUSED}, NULL},
+ {"caption-side", {CSS_TYPE_UNUSED}, NULL},
+ {"clear", {CSS_TYPE_UNUSED}, NULL},
+ {"clip", {CSS_TYPE_UNUSED}, NULL},
+ {"color", {CSS_TYPE_COLOR, CSS_TYPE_UNUSED}, NULL},
+ {"content", {CSS_TYPE_STRING, CSS_TYPE_UNUSED}, NULL},
+ {"counter-increment", {CSS_TYPE_UNUSED}, NULL},
+ {"counter-reset", {CSS_TYPE_UNUSED}, NULL},
+ {"cursor", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_cursor_enum_vals},
+ {"direction", {CSS_TYPE_UNUSED}, NULL},
+ {"display", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_display_enum_vals},
+ {"empty-cells", {CSS_TYPE_UNUSED}, NULL},
+ {"float", {CSS_TYPE_UNUSED}, NULL},
+ {"font-family", {CSS_TYPE_SYMBOL, CSS_TYPE_UNUSED}, NULL},
+ {"font-size", {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED},
+ Css_font_size_enum_vals},
+ {"font-size-adjust", {CSS_TYPE_UNUSED}, NULL},
+ {"font-stretch", {CSS_TYPE_UNUSED}, NULL},
+ {"font-style", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_font_style_enum_vals},
+ {"font-variant", {CSS_TYPE_UNUSED}, NULL},
+ {"font-weight", {CSS_TYPE_ENUM, CSS_TYPE_FONT_WEIGHT, CSS_TYPE_UNUSED},
+ Css_font_weight_enum_vals},
+ {"height", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"left", {CSS_TYPE_UNUSED}, NULL},
+ {"letter-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_letter_spacing_enum_vals},
+ {"line-height",
+ {CSS_TYPE_ENUM, CSS_TYPE_LENGTH_PERCENTAGE_NUMBER, CSS_TYPE_UNUSED},
+ Css_line_height_enum_vals},
+ {"list-style-image", {CSS_TYPE_UNUSED}, NULL},
+ {"list-style-position", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_position_enum_vals},
+ {"list-style-type", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED},
+ Css_list_style_type_enum_vals},
+ {"margin-bottom", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-left", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-right", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"margin-top", {CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"marker-offset", {CSS_TYPE_UNUSED}, NULL},
+ {"marks", {CSS_TYPE_UNUSED}, NULL},
+ {"max-height", {CSS_TYPE_UNUSED}, NULL},
+ {"max-width", {CSS_TYPE_UNUSED}, NULL},
+ {"min-height", {CSS_TYPE_UNUSED}, NULL},
+ {"min-width", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-color", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-style", {CSS_TYPE_UNUSED}, NULL},
+ {"outline-width", {CSS_TYPE_UNUSED}, NULL},
+ {"overflow", {CSS_TYPE_UNUSED}, NULL},
+ {"padding-bottom", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-left", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-right", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"padding-top", {CSS_TYPE_LENGTH, CSS_TYPE_UNUSED}, NULL},
+ {"position", {CSS_TYPE_UNUSED}, NULL},
+ {"quotes", {CSS_TYPE_UNUSED}, NULL},
+ {"right", {CSS_TYPE_UNUSED}, NULL},
+ {"text-align", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_text_align_enum_vals},
+ {"text-decoration", {CSS_TYPE_MULTI_ENUM, CSS_TYPE_UNUSED},
+ Css_text_decoration_enum_vals},
+ {"text-indent", {CSS_TYPE_UNUSED}, NULL},
+ {"text-shadow", {CSS_TYPE_UNUSED}, NULL},
+ {"text-transform", {CSS_TYPE_UNUSED}, NULL},
+ {"top", {CSS_TYPE_UNUSED}, NULL},
+ {"unicode-bidi", {CSS_TYPE_UNUSED}, NULL},
+ {"vertical-align",{CSS_TYPE_ENUM, CSS_TYPE_UNUSED},Css_vertical_align_vals},
+ {"visibility", {CSS_TYPE_UNUSED}, NULL},
+ {"white-space", {CSS_TYPE_ENUM, CSS_TYPE_UNUSED}, Css_white_space_vals},
+ {"width", {CSS_TYPE_LENGTH_PERCENTAGE, CSS_TYPE_UNUSED}, NULL},
+ {"word-spacing", {CSS_TYPE_ENUM, CSS_TYPE_SIGNED_LENGTH, CSS_TYPE_UNUSED},
+ Css_word_spacing_enum_vals},
+ {"z-index", {CSS_TYPE_UNUSED}, NULL},
+
+ /* These are extensions, for internal used, and never parsed. */
+ {"x-link", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-colspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"x-rowspan", {CSS_TYPE_INTEGER, CSS_TYPE_UNUSED}, NULL},
+ {"last", {CSS_TYPE_UNUSED}, NULL},
+};
+
+typedef struct {
+ const char *symbol;
+ enum {
+ CSS_SHORTHAND_MULTIPLE, /* [ p1 || p2 || ...], the property pi is
+ * determined by the type */
+ CSS_SHORTHAND_DIRECTIONS, /* <t>{1,4} */
+ CSS_SHORTHAND_BORDER, /* special, used for 'border' */
+ CSS_SHORTHAND_FONT, /* special, used for 'font' */
+ } type;
+ const CssPropertyName * properties;/* CSS_SHORTHAND_MULTIPLE:
+ * must be terminated by -1
+ * CSS_SHORTHAND_DIRECTIONS:
+ * must have length 4
+ * CSS_SHORTHAND_BORDERS:
+ * must have length 12
+ * CSS_SHORTHAND_FONT:
+ * unused */
+} CssShorthandInfo;
+
+const CssPropertyName Css_background_properties[] = {
+ CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_PROPERTY_BACKGROUND_IMAGE,
+ CSS_PROPERTY_BACKGROUND_REPEAT,
+ CSS_PROPERTY_BACKGROUND_ATTACHMENT,
+ CSS_PROPERTY_BACKGROUND_POSITION,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_bottom_properties[] = {
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_color_properties[4] = {
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_border_left_properties[] = {
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_right_properties[] = {
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_style_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE
+};
+
+const CssPropertyName Css_border_top_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_border_width_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH
+};
+
+const CssPropertyName Css_list_style_properties[] = {
+ CSS_PROPERTY_LIST_STYLE_TYPE,
+ CSS_PROPERTY_LIST_STYLE_POSITION,
+ CSS_PROPERTY_LIST_STYLE_IMAGE,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_margin_properties[] = {
+ CSS_PROPERTY_MARGIN_TOP,
+ CSS_PROPERTY_MARGIN_BOTTOM,
+ CSS_PROPERTY_MARGIN_LEFT,
+ CSS_PROPERTY_MARGIN_RIGHT
+};
+
+const CssPropertyName Css_outline_properties[] = {
+ CSS_PROPERTY_OUTLINE_COLOR,
+ CSS_PROPERTY_OUTLINE_STYLE,
+ CSS_PROPERTY_OUTLINE_WIDTH,
+ (CssPropertyName) - 1
+};
+
+const CssPropertyName Css_padding_properties[] = {
+ CSS_PROPERTY_PADDING_TOP,
+ CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_PROPERTY_PADDING_LEFT,
+ CSS_PROPERTY_PADDING_RIGHT
+};
+
+const CssPropertyName Css_border_properties[] = {
+ CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_PROPERTY_BORDER_TOP_STYLE,
+ CSS_PROPERTY_BORDER_TOP_COLOR,
+ CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_PROPERTY_BORDER_BOTTOM_STYLE,
+ CSS_PROPERTY_BORDER_BOTTOM_COLOR,
+ CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_PROPERTY_BORDER_LEFT_STYLE,
+ CSS_PROPERTY_BORDER_LEFT_COLOR,
+ CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_PROPERTY_BORDER_RIGHT_STYLE,
+ CSS_PROPERTY_BORDER_RIGHT_COLOR
+};
+
+const CssPropertyName Css_font_properties[] = {
+ CSS_PROPERTY_FONT_SIZE,
+ CSS_PROPERTY_FONT_STYLE,
+ CSS_PROPERTY_FONT_VARIANT,
+ CSS_PROPERTY_FONT_WEIGHT,
+ CSS_PROPERTY_FONT_FAMILY,
+ (CssPropertyName) - 1
+};
+
+static const CssShorthandInfo Css_shorthand_info[] = {
+ {"background", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_background_properties},
+ {"border", CssShorthandInfo::CSS_SHORTHAND_BORDER,
+ Css_border_properties},
+ {"border-bottom", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_bottom_properties},
+ {"border-color", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_color_properties},
+ {"border-left", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_left_properties},
+ {"border-right", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_right_properties},
+ {"border-style", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_style_properties},
+ {"border-top", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_border_top_properties},
+ {"border-width", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_border_width_properties},
+ {"font", CssShorthandInfo::CSS_SHORTHAND_FONT,
+ Css_font_properties},
+ {"list-style", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_list_style_properties},
+ {"margin", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_margin_properties},
+ {"outline", CssShorthandInfo::CSS_SHORTHAND_MULTIPLE,
+ Css_outline_properties},
+ {"padding", CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS,
+ Css_padding_properties},
+};
+
+#define CSS_SHORTHAND_NUM \
+ (sizeof(Css_shorthand_info) / sizeof(CssShorthandInfo))
+
+/* ----------------------------------------------------------------------
+ * Parsing
+ * ---------------------------------------------------------------------- */
+
+CssParser::CssParser(CssContext *context, CssOrigin origin,
+ const char *buf, int buflen)
+{
+ this->context = context;
+ this->origin = origin;
+ this->buf = buf;
+ this->buflen = buflen;
+ this->bufptr = 0;
+ this->spaceSeparated = false;
+ this->withinBlock = false;
+
+ nextToken ();
+}
+
+/*
+ * Gets the next character from the buffer, or EOF.
+ */
+int CssParser::getChar()
+{
+ int c;
+
+ if (bufptr >= buflen)
+ c = EOF;
+ else
+ c = buf[bufptr];
+
+ /* The buffer pointer is increased in any case, so that ungetChar works
+ * correctly at the end of the buffer. */
+ bufptr++;
+ return c;
+}
+
+/*
+ * Undoes the last getChar().
+ */
+void CssParser::ungetChar()
+{
+ bufptr--;
+}
+
+/*
+ * Skip string str if it is found in the input buffer.
+ * If not wind back. The first char is passed as parameter c
+ * to avoid unnecessary getChar() / ungetChar() calls.
+ */
+inline bool CssParser::skipString(int c, const char *str)
+{
+ int n = 0;
+
+ while (str[n]) {
+ if (str[n] != c) {
+ while (n--)
+ ungetChar();
+ return false;
+ }
+ c = getChar();
+ n++;
+ }
+
+ return true;
+}
+
+void CssParser::nextToken()
+{
+ int c, c1, d, j;
+ char hexbuf[5];
+ int i = 0;
+
+ ttype = CSS_TK_CHAR; /* init */
+ spaceSeparated = false;
+
+ while (true) {
+ c = getChar();
+ if (isspace(c)) { // ignore whitespace
+ spaceSeparated = true;
+ } else if (skipString(c, "/*")) { // ignore comments
+ do {
+ c = getChar();
+ } while (c != EOF && ! skipString(c, "*/"));
+ } else if (skipString(c, "<!--")) { // ignore XML comment markers
+ } else if (skipString(c, "-->")) {
+ } else {
+ break;
+ }
+ }
+
+ // handle negative numbers
+ if (c == '-') {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ c = getChar();
+ }
+
+ if (isdigit(c)) {
+ ttype = CSS_TK_DECINT;
+ do {
+ if (i < maxStrLen - 1) {
+ tval[i++] = c;
+ }
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+ if (c != '.')
+ ungetChar();
+
+ /* ...but keep going to see whether it's really a float */
+ }
+
+ if (c == '.') {
+ c = getChar();
+ if (isdigit(c)) {
+ ttype = CSS_TK_FLOAT;
+ if (i < maxStrLen - 1)
+ tval[i++] = '.';
+ do {
+ if (i < maxStrLen - 1)
+ tval[i++] = c;
+ /* else silently truncated */
+ c = getChar();
+ } while (isdigit(c));
+
+ ungetChar();
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ } else {
+ ungetChar();
+ if (ttype == CSS_TK_DECINT) {
+ ungetChar();
+ } else {
+ c = '.';
+ }
+ }
+ }
+
+ if (ttype == CSS_TK_DECINT) {
+ tval[i] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token number %s\n", tval);
+ return;
+ }
+
+ if (i) {
+ ungetChar(); /* ungetChar '-' */
+ i--;
+ c = getChar();
+ }
+
+ if (isalpha(c) || c == '_' || c == '-') {
+ ttype = CSS_TK_SYMBOL;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isalnum(c) || c == '_' || c == '-') {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token symbol '%s'\n", tval);
+ return;
+ }
+
+ if (c == '"' || c == '\'') {
+ c1 = c;
+ ttype = CSS_TK_STRING;
+
+ i = 0;
+ c = getChar();
+
+ while (c != EOF && c != c1) {
+ if (c == '\\') {
+ d = getChar();
+ if (isxdigit(d)) {
+ /* Read hex Unicode char. (Actually, strings are yet only 8
+ * bit.) */
+ hexbuf[0] = d;
+ j = 1;
+ d = getChar();
+ while (j < 4 && isxdigit(d)) {
+ hexbuf[j] = d;
+ j++;
+ d = getChar();
+ }
+ hexbuf[j] = 0;
+ ungetChar();
+ c = strtol(hexbuf, NULL, 16);
+ } else {
+ /* Take character literally. */
+ c = d;
+ }
+ }
+
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ /* No ungetChar(). */
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token string '%s'\n", tval);
+ return;
+ }
+
+ /*
+ * Within blocks, '#' starts a color, outside, it is used in selectors.
+ */
+ if (c == '#' && withinBlock) {
+ ttype = CSS_TK_COLOR;
+
+ tval[0] = c;
+ i = 1;
+ c = getChar();
+ while (isxdigit(c)) {
+ if (i < maxStrLen - 1) {
+ tval[i] = c;
+ i++;
+ } /* else silently truncated */
+ c = getChar();
+ }
+ tval[i] = 0;
+ ungetChar();
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token color '%s'\n", tval);
+ return;
+ }
+
+ if (c == EOF) {
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token %s\n", "EOF");
+ ttype = CSS_TK_END;
+ return;
+ }
+
+ ttype = CSS_TK_CHAR;
+ tval[0] = c;
+ tval[1] = 0;
+ DEBUG_MSG(DEBUG_TOKEN_LEVEL, "token char '%c'\n", c);
+}
+
+
+bool CssParser::tokenMatchesProperty(CssPropertyName prop, CssValueType *type)
+{
+ int i, err = 1;
+ CssValueType savedType = *type;
+
+ for (int j = 0; Css_property_info[prop].type[j] != CSS_TYPE_UNUSED; j++) {
+ *type = Css_property_info[prop].type[j];
+
+ switch (Css_property_info[prop].type[j]) {
+
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") == 0)
+ return true;
+ else {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ return true;
+ }
+ }
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ if (tval[0] == '-')
+ return false;
+ // Fall Through
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT ||
+ ttype == CSS_TK_FLOAT ||
+ (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0))
+ return true;
+ break;
+
+ case CSS_TYPE_COLOR:
+ if ((ttype == CSS_TK_COLOR ||
+ ttype == CSS_TK_SYMBOL) &&
+ (dStrcasecmp(tval, "rgb") == 0 ||
+ a_Color_parse(tval, -1, &err) != -1))
+ return true;
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ if (ttype == CSS_TK_SYMBOL ||
+ ttype == CSS_TK_STRING)
+ return true;
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ if (ttype == CSS_TK_DECINT) {
+ i = strtol(tval, NULL, 10);
+ if (i >= 100 && i <= 900)
+ return true;
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false);
+ break;
+ }
+ }
+
+ *type = savedType;
+ return false;
+}
+
+bool CssParser::parseRgbColorComponent(int32_t *cc, int *percentage) {
+ if (ttype != CSS_TK_DECINT) {
+ MSG_CSS("expected integer not found in %s color\n", "rgb");
+ return false;
+ }
+
+ *cc = strtol(tval, NULL, 10);
+
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == '%') {
+ if (*percentage == 0) {
+ MSG_CSS("'%s' unexpected in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 1;
+ *cc = *cc * 255 / 100;
+ nextToken();
+ } else {
+ if (*percentage == 1) {
+ MSG_CSS("expected '%s' not found in rgb color\n", "%");
+ return false;
+ }
+ *percentage = 0;
+ }
+
+ if (*cc > 255)
+ *cc = 255;
+ if (*cc < 0)
+ *cc = 0;
+
+ return true;
+}
+
+bool CssParser::parseRgbColor(int32_t *c) {
+ int32_t cc;
+ int percentage = -1;
+
+ *c = 0;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(') {
+ MSG_CSS("expected '%s' not found in rgb color\n", "(");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 16;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc << 8;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ',') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ",");
+ return false;
+ }
+ nextToken();
+
+ if (!parseRgbColorComponent(&cc, &percentage))
+ return false;
+ *c |= cc;
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ MSG_CSS("expected '%s' not found in rgb color\n", ")");
+ return false;
+ }
+
+ return true;
+}
+
+bool CssParser::parseValue(CssPropertyName prop,
+ CssValueType type,
+ CssPropertyValue * val)
+{
+ CssLengthType lentype;
+ bool found, ret = false;
+ float fval;
+ int i, ival, err = 1;
+ Dstr *dstr;
+
+ switch (type) {
+ case CSS_TYPE_ENUM:
+ if (ttype == CSS_TK_SYMBOL) {
+ for (i = 0; Css_property_info[prop].enum_symbols[i]; i++)
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0) {
+ val->intVal = i;
+ ret = true;
+ break;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_MULTI_ENUM:
+ val->intVal = 0;
+ ret = true;
+
+ while (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "none") != 0) {
+ for (i = 0, found = false;
+ !found && Css_property_info[prop].enum_symbols[i]; i++) {
+ if (dStrcasecmp(tval,
+ Css_property_info[prop].enum_symbols[i]) == 0)
+ val->intVal |= (1 << i);
+ }
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_LENGTH_PERCENTAGE:
+ case CSS_TYPE_LENGTH_PERCENTAGE_NUMBER:
+ case CSS_TYPE_LENGTH:
+ case CSS_TYPE_SIGNED_LENGTH:
+ if (ttype == CSS_TK_DECINT || ttype == CSS_TK_FLOAT) {
+ fval = atof(tval);
+ lentype = CSS_LENGTH_TYPE_NONE;
+
+ nextToken();
+ if (!spaceSeparated && ttype == CSS_TK_SYMBOL) {
+ ret = true;
+
+ if (dStrcasecmp(tval, "px") == 0) {
+ lentype = CSS_LENGTH_TYPE_PX;
+ nextToken();
+ } else if (dStrcasecmp(tval, "mm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "cm") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 10;
+ nextToken();
+ } else if (dStrcasecmp(tval, "in") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= 25.4;
+ nextToken();
+ } else if (dStrcasecmp(tval, "pt") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 72);
+ nextToken();
+ } else if (dStrcasecmp(tval, "pc") == 0) {
+ lentype = CSS_LENGTH_TYPE_MM;
+ fval *= (25.4 / 6);
+ nextToken();
+ } else if (dStrcasecmp(tval, "em") == 0) {
+ lentype = CSS_LENGTH_TYPE_EM;
+ nextToken();
+ } else if (dStrcasecmp(tval, "ex") == 0) {
+ lentype = CSS_LENGTH_TYPE_EX;
+ nextToken();
+ } else {
+ ret = false;
+ }
+ } else if (!spaceSeparated &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE ||
+ type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) &&
+ ttype == CSS_TK_CHAR &&
+ tval[0] == '%') {
+ fval /= 100;
+ lentype = CSS_LENGTH_TYPE_PERCENTAGE;
+ ret = true;
+ nextToken();
+ }
+
+ /* Allow numbers without unit only for 0 or
+ * CSS_TYPE_LENGTH_PERCENTAGE_NUMBER
+ */
+ if (lentype == CSS_LENGTH_TYPE_NONE &&
+ (type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER || fval == 0.0))
+ ret = true;
+
+ val->intVal = CSS_CREATE_LENGTH(fval, lentype);
+ } else if (ttype == CSS_TK_SYMBOL && dStrcasecmp(tval, "auto") == 0) {
+ ret = true;
+ val->intVal = CSS_LENGTH_TYPE_AUTO;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_COLOR:
+ if (ttype == CSS_TK_COLOR) {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ nextToken();
+ } else if (ttype == CSS_TK_SYMBOL) {
+ if (dStrcasecmp(tval, "rgb") == 0) {
+ nextToken();
+ if (parseRgbColor(&val->intVal))
+ ret = true;
+ else
+ MSG_CSS("Failed to parse %s color\n", "rgb(r,g,b)");
+ } else {
+ val->intVal = a_Color_parse(tval, -1, &err);
+ if (err)
+ MSG_CSS("color is not in \"%s\" format\n", "#RRGGBB");
+ else
+ ret = true;
+ }
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_STRING:
+ if (ttype == CSS_TK_STRING) {
+ val->strVal = dStrdup(tval);
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_SYMBOL:
+ /* Read comma separated list of font family names */
+ dstr = dStr_new("");
+ while (ttype == CSS_TK_SYMBOL || ttype == CSS_TK_STRING ||
+ (ttype == CSS_TK_CHAR && tval[0] == ',')) {
+ if (spaceSeparated)
+ dStr_append_c(dstr, ' ');
+ dStr_append(dstr, tval);
+ ret = true;
+ nextToken();
+ }
+
+ if (ret) {
+ val->strVal = dStrstrip(dstr->str);
+ dStr_free(dstr, 0);
+ } else {
+ dStr_free(dstr, 1);
+ }
+ break;
+
+ case CSS_TYPE_FONT_WEIGHT:
+ ival = 0;
+ if (ttype == CSS_TK_DECINT) {
+ ival = strtol(tval, NULL, 10);
+ if (ival < 100 || ival > 900)
+ /* invalid */
+ ival = 0;
+ }
+
+ if (ival != 0) {
+ val->intVal = ival;
+ ret = true;
+ nextToken();
+ }
+ break;
+
+ case CSS_TYPE_UNUSED:
+ /* nothing */
+ break;
+
+ case CSS_TYPE_INTEGER:
+ /* Not used for parser values. */
+ default:
+ assert(false); /* not reached */
+ }
+
+ return ret;
+}
+
+bool CssParser::parseWeight()
+{
+ if (ttype == CSS_TK_CHAR && tval[0] == '!') {
+ nextToken();
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "important") == 0) {
+ nextToken();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * bsearch(3) compare function for searching properties
+ */
+static int Css_property_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssPropertyInfo *) a)->symbol,
+ ((CssPropertyInfo *) b)->symbol);
+}
+
+
+/*
+ * bsearch(3) compare function for searching shorthands
+ */
+static int Css_shorthand_info_cmp(const void *a, const void *b)
+{
+ return dStrcasecmp(((CssShorthandInfo *) a)->symbol,
+ ((CssShorthandInfo *) b)->symbol);
+}
+
+void CssParser::parseDeclaration(CssPropertyList * props,
+ CssPropertyList * importantProps)
+{
+ CssPropertyInfo pi = {NULL, {CSS_TYPE_UNUSED}, NULL}, *pip;
+ CssShorthandInfo si, *sip;
+ CssValueType type = CSS_TYPE_UNUSED;
+
+ CssPropertyName prop;
+ CssPropertyValue val, dir_vals[4];
+ CssValueType dir_types[4];
+ bool found, weight;
+ int sh_index, i, j, n;
+ int dir_set[4][4] = {
+ /* 1 value */ {0, 0, 0, 0},
+ /* 2 values */ {0, 0, 1, 1},
+ /* 3 values */ {0, 2, 1, 1},
+ /* 4 values */ {0, 2, 3, 1}
+ };
+
+ if (ttype == CSS_TK_SYMBOL) {
+ pi.symbol = tval;
+ pip =
+ (CssPropertyInfo *) bsearch(&pi, Css_property_info,
+ CSS_NUM_PARSED_PROPERTIES,
+ sizeof(CssPropertyInfo),
+ Css_property_info_cmp);
+ if (pip) {
+ prop = (CssPropertyName) (pip - Css_property_info);
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+ if (tokenMatchesProperty (prop, &type) &&
+ parseValue(prop, type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->set(prop, type, val);
+ else
+ props->set(prop, type, val);
+ }
+ }
+ } else {
+ /* Try shorthands. */
+ si.symbol = tval;
+ sip =
+ (CssShorthandInfo *) bsearch(&pi, Css_shorthand_info,
+ CSS_SHORTHAND_NUM,
+ sizeof(CssShorthandInfo),
+ Css_shorthand_info_cmp);
+ if (sip) {
+ sh_index = sip - Css_shorthand_info;
+ nextToken();
+ if (ttype == CSS_TK_CHAR && tval[0] == ':') {
+ nextToken();
+
+ switch (Css_shorthand_info[sh_index].type) {
+
+ case CssShorthandInfo::CSS_SHORTHAND_FONT:
+ /* \todo Implement details. */
+ case CssShorthandInfo::CSS_SHORTHAND_MULTIPLE:
+ do {
+ for (found = false, i = 0;
+ !found &&
+ Css_shorthand_info[sh_index].properties[i] != -1;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ DEBUG_MSG(DEBUG_PARSE_LEVEL,
+ "will assign to '%s'\n",
+ Css_property_info
+ [Css_shorthand_info[sh_index]
+ .properties[i]].symbol);
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[i], type, val);
+ }
+ }
+ } while (found);
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_DIRECTIONS:
+ n = 0;
+ while (n < 4) {
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[0], &type) &&
+ parseValue(Css_shorthand_info[sh_index]
+ .properties[0], type, &val)) {
+ dir_vals[n] = val;
+ dir_types[n] = type;
+ n++;
+ } else
+ break;
+ }
+
+ weight = parseWeight();
+ if (n > 0) {
+ for (i = 0; i < 4; i++)
+ if (weight && importantProps)
+ importantProps->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ else
+ props->set(Css_shorthand_info[sh_index]
+ .properties[i],
+ dir_types[dir_set[n - 1][i]],
+ dir_vals[dir_set[n - 1][i]]);
+ } else
+ MSG_CSS("no values for shorthand property '%s'\n",
+ Css_shorthand_info[sh_index].symbol);
+
+ break;
+
+ case CssShorthandInfo::CSS_SHORTHAND_BORDER:
+ do {
+ for (found = false, i = 0;
+ !found && i < 3;
+ i++)
+ if (tokenMatchesProperty(Css_shorthand_info[sh_index].
+ properties[i], &type)) {
+ found = true;
+ if (parseValue(Css_shorthand_info[sh_index]
+ .properties[i], type, &val)) {
+ weight = parseWeight();
+ for (j = 0; j < 4; j++)
+ if (weight && importantProps)
+ importantProps->
+ set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ else
+ props->set(Css_shorthand_info[sh_index].
+ properties[j * 3 + i], type, val);
+ }
+ }
+ } while (found);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR &&
+ (tval[0] == ';' || tval[0] == '}'))))
+ nextToken();
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ';')
+ nextToken();
+}
+
+bool CssParser::parseSimpleSelector(CssSimpleSelector *selector)
+{
+ CssSimpleSelector::SelectType selectType;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setElement (a_Html_tag_index(tval));
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '*') {
+ selector->setElement (CssSimpleSelector::ELEMENT_ANY);
+ nextToken();
+ if (spaceSeparated)
+ return true;
+ } else if (ttype == CSS_TK_CHAR &&
+ (tval[0] == '#' ||
+ tval[0] == '.' ||
+ tval[0] == ':')) {
+ // nothing to be done in this case
+ } else {
+ return false;
+ }
+
+ do {
+ selectType = CssSimpleSelector::SELECT_NONE;
+ if (ttype == CSS_TK_CHAR) {
+ switch (tval[0]) {
+ case '#':
+ selectType = CssSimpleSelector::SELECT_ID;
+ break;
+ case '.':
+ selectType = CssSimpleSelector::SELECT_CLASS;
+ break;
+ case ':':
+ selectType = CssSimpleSelector::SELECT_PSEUDO_CLASS;
+ if (selector->getPseudoClass ())
+ // pseudo class has been set already.
+ // As dillo currently only supports :link and :visisted, a
+ // selector with more than one pseudo class will never match.
+ // By returning false, the whole CssRule will be dropped.
+ // \todo adapt this when supporting :hover, :active...
+ return false;
+ break;
+ }
+ }
+
+ if (selectType != CssSimpleSelector::SELECT_NONE) {
+ nextToken();
+ if (spaceSeparated)
+ return true;
+
+ if (ttype == CSS_TK_SYMBOL) {
+ selector->setSelect (selectType, tval);
+ nextToken();
+ } else {
+ return false; // don't accept classes or id's starting with integer
+ }
+ if (spaceSeparated)
+ return true;
+ }
+ } while (selectType != CssSimpleSelector::SELECT_NONE);
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of simple selector (%s, %s, %s, %d)\n",
+ selector->id, selector->klass,
+ selector->pseudo, selector->element);
+
+ return true;
+}
+
+CssSelector *CssParser::parseSelector()
+{
+ CssSelector *selector = new CssSelector ();
+
+ while (true) {
+ if (! parseSimpleSelector (selector->top ())) {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+
+ if (ttype == CSS_TK_CHAR &&
+ (tval[0] == ',' || tval[0] == '{')) {
+ break;
+ } else if (ttype == CSS_TK_CHAR && tval[0] == '>') {
+ selector->addSimpleSelector (CssSelector::CHILD);
+ nextToken();
+ } else if (ttype != CSS_TK_END && spaceSeparated) {
+ selector->addSimpleSelector (CssSelector::DESCENDANT);
+ } else {
+ delete selector;
+ selector = NULL;
+ break;
+ }
+ }
+
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR ||
+ (tval[0] != ',' && tval[0] != '{')))
+ nextToken();
+
+ return selector;
+}
+
+void CssParser::parseRuleset()
+{
+ lout::misc::SimpleVector < CssSelector * >*list;
+ CssPropertyList *props, *importantProps;
+ CssSelector *selector;
+
+ list = new lout::misc::SimpleVector < CssSelector * >(1);
+
+ while (true) {
+ selector = parseSelector();
+
+ if (selector) {
+ selector->ref();
+ list->increase();
+ list->set(list->size() - 1, selector);
+ }
+
+ // \todo dump whole ruleset in case of parse error as required by CSS 2.1
+ // however make sure we don't dump it if only dillo fails to parse
+ // valid CSS.
+
+ if (ttype == CSS_TK_CHAR && tval[0] == ',')
+ /* To read the next token. */
+ nextToken();
+ else
+ /* No more selectors. */
+ break;
+ }
+
+ DEBUG_MSG(DEBUG_PARSE_LEVEL, "end of %s\n", "selectors");
+
+ props = new CssPropertyList(true);
+ props->ref();
+ importantProps = new CssPropertyList(true);
+ importantProps->ref();
+
+ /* Read block. ('{' has already been read.) */
+ if (ttype != CSS_TK_END) {
+ withinBlock = true;
+ nextToken();
+ do
+ parseDeclaration(props, importantProps);
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && tval[0] == '}')));
+ withinBlock = false;
+ }
+
+ for (int i = 0; i < list->size(); i++) {
+ CssSelector *s = list->get(i);
+
+ if (origin == CSS_ORIGIN_USER_AGENT) {
+ context->addRule(s, props, CSS_PRIMARY_USER_AGENT);
+ } else if (origin == CSS_ORIGIN_USER) {
+ context->addRule(s, props, CSS_PRIMARY_USER);
+ context->addRule(s, importantProps, CSS_PRIMARY_USER_IMPORTANT);
+ } else if (origin == CSS_ORIGIN_AUTHOR) {
+ context->addRule(s, props, CSS_PRIMARY_AUTHOR);
+ context->addRule(s, importantProps, CSS_PRIMARY_AUTHOR_IMPORTANT);
+ }
+
+ s->unref();
+ }
+
+ props->unref();
+ importantProps->unref();
+
+ delete list;
+
+ if (ttype == CSS_TK_CHAR && tval[0] == '}')
+ nextToken();
+}
+
+char * CssParser::parseUrl()
+{
+ Dstr *urlStr = NULL;
+
+ if (ttype != CSS_TK_SYMBOL ||
+ dStrcasecmp(tval, "url") != 0)
+ return NULL;
+
+ nextToken();
+
+ if (ttype != CSS_TK_CHAR || tval[0] != '(')
+ return NULL;
+
+ nextToken();
+
+ if (ttype == CSS_TK_STRING) {
+ urlStr = dStr_new(tval);
+ nextToken();
+ } else {
+ urlStr = dStr_new("");
+ while (ttype != CSS_TK_END &&
+ (ttype != CSS_TK_CHAR || tval[0] != ')')) {
+ dStr_append(urlStr, tval);
+ nextToken();
+ }
+ }
+
+ if (ttype != CSS_TK_CHAR || tval[0] != ')') {
+ dStr_free(urlStr, 1);
+ urlStr = NULL;
+ }
+
+ if (urlStr) {
+ char *url = urlStr->str;
+ dStr_free(urlStr, 0);
+ return url;
+ } else {
+ return NULL;
+ }
+}
+
+void CssParser::parseImport(DilloHtml *html, DilloUrl *baseUrl)
+{
+ char *urlStr = NULL;
+
+ if (html != NULL &&
+ ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "import") == 0) {
+ nextToken();
+
+ if (ttype == CSS_TK_SYMBOL &&
+ dStrcasecmp(tval, "url") == 0)
+ urlStr = parseUrl();
+ else if (ttype == CSS_TK_STRING)
+ urlStr = dStrdup (tval);
+
+ /* Skip all tokens until the expected end. */
+ while (!(ttype == CSS_TK_END ||
+ (ttype == CSS_TK_CHAR && (tval[0] == ';'))))
+ nextToken();
+
+ nextToken();
+
+ if (urlStr) {
+ MSG("CssParser::parseImport(): @import %s\n", urlStr);
+ DilloUrl *url = a_Html_url_new (html, urlStr, a_Url_str(baseUrl),
+ baseUrl ? 1 : 0);
+ a_Html_load_stylesheet(html, url);
+ a_Url_free(url);
+ dFree (urlStr);
+ }
+ }
+}
+
+const char * CssParser::propertyNameString(CssPropertyName name)
+{
+ return Css_property_info[name].symbol;
+}
+
+void CssParser::parse(DilloHtml *html, DilloUrl *url, CssContext * context,
+ const char *buf,
+ int buflen, CssOrigin origin)
+{
+ CssParser parser (context, origin, buf, buflen);
+
+ while (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '@') {
+ parser.nextToken();
+ parser.parseImport(html, url);
+ }
+
+ while (parser.ttype != CSS_TK_END)
+ parser.parseRuleset();
+}
+
+CssPropertyList *CssParser::parseDeclarationBlock(const char *buf, int buflen)
+{
+ CssPropertyList *props = new CssPropertyList (true);
+ CssParser parser (NULL, CSS_ORIGIN_AUTHOR, buf, buflen);
+
+ parser.withinBlock = true;
+
+ do
+ parser.parseDeclaration(props, NULL);
+ while (!(parser.ttype == CSS_TK_END ||
+ (parser.ttype == CSS_TK_CHAR && parser.tval[0] == '}')));
+
+ if (props->size () == 0) {
+ delete props;
+ props = NULL;
+ }
+
+ return props;
+}
diff --git a/src/cssparser.hh b/src/cssparser.hh
new file mode 100644
index 00000000..1e471c68
--- /dev/null
+++ b/src/cssparser.hh
@@ -0,0 +1,54 @@
+#ifndef __CSSPARSER_HH__
+#define __CSSPARSER_HH__
+
+#include "css.hh"
+#include "html_common.hh"
+
+class CssParser {
+ private:
+ typedef enum {
+ CSS_TK_DECINT, CSS_TK_FLOAT, CSS_TK_COLOR, CSS_TK_SYMBOL,
+ CSS_TK_STRING, CSS_TK_CHAR, CSS_TK_END
+ } CssTokenType;
+
+ static const int maxStrLen = 256;
+ CssContext *context;
+ CssOrigin origin;
+
+ const char *buf;
+ int buflen, bufptr;
+
+ CssTokenType ttype;
+ char tval[maxStrLen];
+ bool withinBlock;
+ bool spaceSeparated; /* used when parsing CSS selectors */
+
+ CssParser(CssContext *context, CssOrigin origin,
+ const char *buf, int buflen);
+ int getChar();
+ void ungetChar();
+ void nextToken();
+ bool skipString(int c, const char *string);
+ bool tokenMatchesProperty(CssPropertyName prop, CssValueType * type);
+ bool parseValue(CssPropertyName prop, CssValueType type,
+ CssPropertyValue * val);
+ bool parseWeight();
+ bool parseRgbColorComponent(int32_t *cc, int *percentage);
+ bool parseRgbColor(int32_t *c);
+ void parseDeclaration(CssPropertyList * props,
+ CssPropertyList * importantProps);
+ bool parseSimpleSelector(CssSimpleSelector *selector);
+ char *parseUrl();
+ void parseImport(DilloHtml *html, DilloUrl *url);
+ CssSelector *parseSelector();
+ void parseRuleset();
+
+ public:
+ static CssPropertyList *parseDeclarationBlock(const char *buf,
+ int buflen);
+ static void parse(DilloHtml *html, DilloUrl *url, CssContext *context,
+ const char *buf, int buflen, CssOrigin origin);
+ static const char *propertyNameString(CssPropertyName name);
+};
+
+#endif
diff --git a/src/decode.c b/src/decode.c
index ff4fcb27..24067318 100644
--- a/src/decode.c
+++ b/src/decode.c
@@ -15,6 +15,7 @@
#include <stdlib.h> /* strtol */
#include "decode.h"
+#include "utf8.hh"
#include "msg.h"
static const int bufsize = 8*1024;
@@ -106,13 +107,12 @@ static Dstr *Decode_gzip(Decode *dc, const char *instr, int inlen)
rc = inflate(zs, Z_SYNC_FLUSH);
+ dStr_append_l(output, dc->buffer, zs->total_out);
+
if ((rc == Z_OK) || (rc == Z_STREAM_END)) {
// Z_STREAM_END at end of file
inputConsumed += zs->total_in;
-
- dStr_append_l(output, dc->buffer, zs->total_out);
-
zs->total_out = 0;
zs->total_in = 0;
} else if (rc == Z_DATA_ERROR) {
@@ -126,6 +126,7 @@ static void Decode_gzip_free(Decode *dc)
{
(void)inflateEnd((z_stream *)dc->state);
+ dFree(dc->state);
dFree(dc->buffer);
}
@@ -164,15 +165,8 @@ static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
if (rc == EILSEQ){
inPtr++;
inLeft--;
- /*
- * U+FFFD: "used to replace an incoming character whose value is
- * unknown or unrepresentable in Unicode."
- */
- //dStr_append(output, "\ufffd");
- // \uxxxx is C99. UTF-8-specific:
- dStr_append_c(output, 0xEF);
- dStr_append_c(output, 0xBF);
- dStr_append_c(output, 0xBD);
+ dStr_append_l(output, utf8_replacement_char,
+ sizeof(utf8_replacement_char) - 1);
}
}
dStr_erase(dc->leftover, 0, dc->leftover->len - inLeft);
@@ -182,6 +176,7 @@ static Dstr *Decode_charset(Decode *dc, const char *instr, int inlen)
static void Decode_charset_free(Decode *dc)
{
+ /* iconv_close() frees dc->state */
(void)iconv_close((iconv_t)(dc->state));
dFree(dc->buffer);
@@ -195,7 +190,7 @@ Decode *a_Decode_transfer_init(const char *format)
{
Decode *dc = NULL;
- if (format && !dStrncasecmp(format, "chunked", 7)) {
+ if (format && !dStrcasecmp(format, "chunked")) {
int *chunk_remaining = dNew(int, 1);
*chunk_remaining = 0;
dc = dNew(Decode, 1);
@@ -221,9 +216,9 @@ Decode *a_Decode_content_init(const char *format)
if (format && *format) {
if (!dStrcasecmp(format, "gzip") || !dStrcasecmp(format, "x-gzip")) {
+ z_stream *zs;
_MSG("gzipped data!\n");
- z_stream *zs;
dc = dNew(Decode, 1);
dc->buffer = dNew(char, bufsize);
dc->state = zs = dNew(z_stream, 1);
@@ -242,7 +237,7 @@ Decode *a_Decode_content_init(const char *format)
MSG("Content-Encoding '%s' not recognized.\n", format);
}
}
- return dc;
+ return dc;
}
/*
@@ -289,10 +284,10 @@ Decode *a_Decode_charset_init(const char *format)
dc->decode = Decode_charset;
dc->free = Decode_charset_free;
} else {
- MSG("Unable to convert from character encoding: '%s'\n", format);
+ MSG_WARN("Unable to convert from character encoding: '%s'\n", format);
}
}
- return dc;
+ return dc;
}
/*
diff --git a/src/dgif.h b/src/dgif.h
new file mode 100644
index 00000000..ec36812d
--- /dev/null
+++ b/src/dgif.h
@@ -0,0 +1,19 @@
+#ifndef __GIF_H__
+#define __GIF_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Gif_callback(int Op, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__GIF_H__ */
diff --git a/src/dialog.cc b/src/dialog.cc
index 69781f32..47af9921 100644
--- a/src/dialog.cc
+++ b/src/dialog.cc
@@ -11,7 +11,7 @@
// UI dialogs
-#include <math.h> // for rint()
+#include <math.h> // for rint()
#include <fltk/Window.h>
#include <fltk/ask.h>
@@ -20,6 +20,9 @@
#include <fltk/ReturnButton.h>
#include <fltk/TextDisplay.h>
#include <fltk/HighlightButton.h>
+#include <fltk/WordwrapOutput.h>
+#include <fltk/Input.h>
+#include <fltk/SecretInput.h>
#include "msg.h"
#include "dialog.hh"
@@ -118,19 +121,19 @@ char *a_Dialog_open_file(const char *msg,
}
/*
- * Make a new window with the provided text
+ * Show a new window with the provided text
*/
-void *a_Dialog_make_text_window(const char *txt, const char *title)
+void a_Dialog_text_window(const char *txt, const char *title)
{
//int wh = 600, ww = 650, bh = 30;
int wh = prefs.height, ww = prefs.width, bh = 30;
int lines, line_num_width;
- Font *textfont = font(prefs.fw_fontname, 0);
-
+ Font *textfont = font(prefs.font_monospace, 0);
+
Window *window = new Window(ww, wh, title ? title : "Untitled");
window->callback(window_close_cb, window);
window->begin();
-
+
TextDisplay *td = new TextDisplay(0,0,ww, wh-bh);
td->buffer()->text(txt);
@@ -156,15 +159,7 @@ void *a_Dialog_make_text_window(const char *txt, const char *title)
window->resizable(td);
window->end();
- return window;
-}
-
-/*
- * Show a window.
- */
-void a_Dialog_show_text_window(void *vWindow)
-{
- ((Window *)vWindow)->show();
+ window->show();
}
/*--------------------------------------------------------------------------*/
@@ -193,7 +188,7 @@ int a_Dialog_choice5(const char *QuestionTxt,
txt[0] = txt[6] = NULL;
txt[1] = alt1; txt[2] = alt2; txt[3] = alt3;
txt[4] = alt4; txt[5] = alt5;
- for (int i=1; txt[i]; ++i, ++nb);
+ for (int i=1; txt[i]; ++i, ++nb) ;
Window *window = new Window(ww,wh,"Choice5");
window->begin();
@@ -214,7 +209,7 @@ int a_Dialog_choice5(const char *QuestionTxt,
b = new HighlightButton(xpos, wh-bh, bw, bh, txt[i]);
b->align(ALIGN_WRAP|ALIGN_CLIP);
b->box(UP_BOX);
- b->callback(choice5_cb, (void*)i);
+ b->callback(choice5_cb, INT2VOIDP(i));
xpos += bw + gap;
}
window->end();
@@ -227,3 +222,81 @@ int a_Dialog_choice5(const char *QuestionTxt,
return choice5_answer;
}
+
+/*--------------------------------------------------------------------------*/
+/*
+ * ret: 0 = Cancel, 1 = OK
+ */
+static void Dialog_user_password_cb(Widget *button, void *vIntPtr)
+{
+ int ret = VOIDP2INT(vIntPtr);
+ _MSG("Dialog_user_password_cb: %d\n", ret);
+ button->window()->make_exec_return(ret);
+}
+
+/*
+ * Make a user/password dialog.
+ * Call the callback with the result (OK or not) and the given user and
+ * password if OK.
+ */
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp)
+{
+ int ok,
+ window_w = 300, window_h = 280,
+ input_x = 80, input_w = 200, input_h = 30,
+ button_y = 230, button_h = 30;
+
+ Window *window =
+ new Window(window_w,window_h,"User/Password");
+ window->begin();
+
+ /* message */
+ WordwrapOutput *message_output =
+ new WordwrapOutput(20,20,window_w-40,100);
+ message_output->box(DOWN_BOX);
+ message_output->text(message);
+ message_output->textfont(HELVETICA_BOLD_ITALIC);
+ message_output->textsize(14);
+
+ /* inputs */
+ Input *user_input =
+ new Input(input_x,140,input_w,input_h,"User");
+ user_input->labelsize(14);
+ user_input->textsize(14);
+ SecretInput *password_input =
+ new SecretInput(input_x,180,input_w,input_h,"Password");
+ password_input->labelsize(14);
+ password_input->textsize(14);
+
+ /* "OK" button */
+ Button *ok_button =
+ new Button(200,button_y,50,button_h,"OK");
+ ok_button->labelsize(14);
+ ok_button->callback(Dialog_user_password_cb);
+ ok_button->user_data(INT2VOIDP(1));
+
+ /* "Cancel" button */
+ Button *cancel_button =
+ new Button(50,button_y,100,button_h,"Cancel");
+ cancel_button->labelsize(14);
+ cancel_button->callback(Dialog_user_password_cb);
+ cancel_button->user_data(INT2VOIDP(0));
+
+ window->end();
+ window->size_range(window_w,window_h,window_w,window_h);
+ window->resizable(window);
+
+ if ((ok = window->exec())) {
+ /* call the callback */
+ const char *user, *password;
+ user = user_input->value();
+ password = password_input->value();
+ _MSG("a_Dialog_user_passwd: ok = %d\n", ok);
+ (*cb)(user, password, vp);
+ }
+
+ delete window;
+
+ return ok;
+}
+
diff --git a/src/dialog.hh b/src/dialog.hh
index 17c326cb..440e9bba 100644
--- a/src/dialog.hh
+++ b/src/dialog.hh
@@ -5,12 +5,16 @@
extern "C" {
#endif /* __cplusplus */
+typedef void (*UserPasswordCB)(const char *user, const char *password,
+ void *vp);
+
void a_Dialog_msg(const char *msg);
int a_Dialog_choice3(const char *msg,
const char *b0, const char *b1, const char *b2);
int a_Dialog_choice5(const char *QuestionTxt,
const char *alt1, const char *alt2, const char *alt3,
const char *alt4, const char *alt5);
+int a_Dialog_user_password(const char *message, UserPasswordCB cb, void *vp);
const char *a_Dialog_input(const char *msg);
const char *a_Dialog_passwd(const char *msg);
const char *a_Dialog_save_file(const char *msg,
@@ -19,8 +23,7 @@ const char *a_Dialog_select_file(const char *msg,
const char *pattern, const char *fname);
char *a_Dialog_open_file(const char *msg,
const char *pattern, const char *fname);
-void *a_Dialog_make_text_window(const char *txt, const char *title);
-void a_Dialog_show_text_window(void *vWindow);
+void a_Dialog_text_window(const char *txt, const char *title);
#ifdef __cplusplus
}
diff --git a/src/dicache.c b/src/dicache.c
index 97ed1915..e700f000 100644
--- a/src/dicache.c
+++ b/src/dicache.c
@@ -9,18 +9,26 @@
* (at your option) any later version.
*/
-#include <sys/time.h> /* for libc5 compatibility */
#include <string.h> /* for memset */
-#include <stdio.h>
#include <stdlib.h>
+#include "msg.h"
#include "image.hh"
+#include "imgbuf.hh"
#include "web.hh"
#include "dicache.h"
-#include "cache.h"
+#include "dpng.h"
+#include "dgif.h"
+#include "djpeg.h"
typedef struct _DICacheNode DICacheNode;
+enum {
+ DIC_Gif,
+ DIC_Png,
+ DIC_Jpeg
+};
+
struct _DICacheNode {
int valid; /* flag */
DilloUrl *url; /* primary "Key" for this dicache entry */
@@ -33,9 +41,9 @@ struct _DICacheNode {
*/
static Dlist *CachedIMGs = NULL;
-static int dicache_size_total; /* invariant: dicache_size_total is
- * the sum of the image sizes (3*w*h)
- * of all the images in the dicache. */
+static uint_t dicache_size_total; /* invariant: dicache_size_total is
+ * the sum of the image sizes (3*w*h)
+ * of all the images in the dicache. */
/*
* Compare two dicache nodes
@@ -78,15 +86,18 @@ static DICacheEntry *Dicache_entry_new(void)
entry->height = 0;
entry->type = DILLO_IMG_TYPE_NOTSET;
entry->cmap = NULL;
- entry->linebuf = NULL;
entry->v_imgbuf = NULL;
entry->RefCount = 1;
entry->TotalSize = 0;
- entry->Y = 0;
entry->ScanNumber = 0;
entry->BitVec = NULL;
entry->State = DIC_Empty;
- entry->version = 0;
+ entry->version = 1;
+
+ entry->Decoder = NULL;
+ entry->DecoderData = NULL;
+ entry->DecodedSize = 0;
+
entry->next = NULL;
return entry;
@@ -96,7 +107,7 @@ static DICacheEntry *Dicache_entry_new(void)
* Add a new entry in the dicache
* (a single node (URL) may have several entries)
*/
-DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
+static DICacheEntry *Dicache_add_entry(const DilloUrl *Url)
{
DICacheEntry *entry;
DICacheNode *node;
@@ -127,41 +138,31 @@ DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url)
}
/*
- * Search an entry in the dicache (given the Url).
- * Return value: a pointer to the entry of the _newest_ (i.e. highest)
- * version if found; NULL otherwise.
- */
-DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url)
-{
- DICacheNode *node;
- DICacheEntry *entry;
-
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
-
- if (!node || !node->valid)
- return NULL;
-
- for (entry = node->first; (entry && entry->next); entry = entry->next);
-
- return entry;
-}
-
-/*
* Search a particular version of a URL in the Dicache.
* Return value: a pointer to the entry if found; NULL otherwise.
+ *
+ * Notes: DIC_Last means last version of the image.
+ * version zero is not allowed.
*/
-static DICacheEntry *Dicache_get_entry_version(const DilloUrl *Url,
- int version)
+DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version)
{
DICacheNode *node;
- DICacheEntry *entry;
+ DICacheEntry *entry = NULL;
- node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
- entry = (node) ? node->first : NULL;
-
- while (entry && entry->version != version)
- entry = entry->next;
+ dReturn_val_if_fail(version != 0, NULL);
+ node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
+ if (node) {
+ if (version == DIC_Last) {
+ if (node->valid) {
+ entry = node->first;
+ for ( ; (entry && entry->next); entry = entry->next);
+ }
+ } else {
+ entry = node->first;
+ for ( ; entry && entry->version != version; entry = entry->next) ;
+ }
+ }
return entry;
}
@@ -172,7 +173,7 @@ static void Dicache_remove(const DilloUrl *Url, int version)
{
DICacheNode *node;
DICacheEntry *entry, *prev;
-
+ _MSG("Dicache_remove url=%s\n", URL_STR(Url));
node = dList_find_sorted(CachedIMGs, Url, Dicache_node_by_url_cmp);
prev = entry = (node) ? node->first : NULL;
@@ -182,11 +183,15 @@ static void Dicache_remove(const DilloUrl *Url, int version)
}
if (entry) {
+ _MSG("Dicache_remove Decoder=%p DecoderData=%p\n",
+ entry->Decoder, entry->DecoderData);
/* Eliminate this dicache entry */
dFree(entry->cmap);
- dFree(entry->linebuf);
a_Bitvec_free(entry->BitVec);
- a_Image_imgbuf_unref(entry->v_imgbuf);
+ a_Imgbuf_unref(entry->v_imgbuf);
+ if (entry->Decoder) {
+ entry->Decoder(CA_Abort, entry->DecoderData);
+ }
dicache_size_total -= entry->TotalSize;
if (node->first == entry) {
@@ -206,14 +211,14 @@ static void Dicache_remove(const DilloUrl *Url, int version)
/*
* Unrefs the counter of a dicache entry, and _if_ no DwImage is acessing
- * this buffer, then we call Dicache_free to do the dirty job.
+ * this buffer, then we call Dicache_remove() to do the job.
*/
void a_Dicache_unref(const DilloUrl *Url, int version)
{
DICacheEntry *entry;
- if ((entry = Dicache_get_entry_version(Url, version))) {
- /*if (--entry->RefCount == 0 && (entry->next || !prefs.use_dicache)) {*/
+ _MSG("a_Dicache_unref\n");
+ if ((entry = a_Dicache_get_entry(Url, version))) {
if (--entry->RefCount == 0) {
Dicache_remove(Url, version);
}
@@ -223,12 +228,11 @@ void a_Dicache_unref(const DilloUrl *Url, int version)
/*
* Refs the counter of a dicache entry.
*/
-
DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
{
DICacheEntry *entry;
- if ((entry = Dicache_get_entry_version(Url, version))) {
+ if ((entry = a_Dicache_get_entry(Url, version))) {
++entry->RefCount;
}
return entry;
@@ -236,7 +240,8 @@ DICacheEntry* a_Dicache_ref(const DilloUrl *Url, int version)
/*
* Invalidate this entry. This is used for the reloading mechanism.
- * Can't erase current versions, but a_Dicache_get_entry must return NULL.
+ * Can't erase current versions, but a_Dicache_get_entry(url, DIC_Last)
+ * must return NULL.
*/
void a_Dicache_invalidate_entry(const DilloUrl *Url)
{
@@ -251,57 +256,6 @@ void a_Dicache_invalidate_entry(const DilloUrl *Url)
/* ------------------------------------------------------------------------- */
/*
- * This function is a cache client; (but feeds its clients from dicache)
- */
-void a_Dicache_callback(int Op, CacheClient_t *Client)
-{
- /* TODO: Handle Op = CA_Abort (to show what was got) --Jcid */
- uint_t i;
- DilloWeb *Web = Client->Web;
- DilloImage *Image = Web->Image;
- DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url);
-
- dReturn_if_fail ( DicEntry != NULL );
-
- /* when the data stream is not an image 'v_imgbuf' remains NULL */
- if (Op == CA_Send && DicEntry->v_imgbuf) {
- if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
- /* Set parms */
- a_Image_set_parms(
- Image, DicEntry->v_imgbuf, DicEntry->url,
- DicEntry->version, DicEntry->width, DicEntry->height,
- DicEntry->type);
- }
- if (DicEntry->State == DIC_Write) {
- if (DicEntry->ScanNumber == Image->ScanNumber) {
- for (i = 0; i < DicEntry->height; ++i)
- if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
- !a_Bitvec_get_bit(Image->BitVec, (int)i) )
- a_Image_write(Image, DicEntry->v_imgbuf,
- DicEntry->linebuf, i, FALSE);
- } else {
- for (i = 0; i < DicEntry->height; ++i) {
- if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
- !a_Bitvec_get_bit(Image->BitVec, (int)i) ||
- DicEntry->ScanNumber > Image->ScanNumber + 1) {
- a_Image_write(Image, DicEntry->v_imgbuf,
- DicEntry->linebuf, i, FALSE);
- }
- if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
- a_Bitvec_clear_bit(Image->BitVec, (int)i);
- }
- Image->ScanNumber = DicEntry->ScanNumber;
- }
- }
- } else if (Op == CA_Close || Op == CA_Abort) {
- a_Image_close(Web->Image);
- a_Bw_close_client(Web->bw, Client->Key);
- }
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
* Set image's width, height & type
* (By now, we'll use the image information despite the html tags --Jcid)
*/
@@ -309,37 +263,29 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
uint_t width, uint_t height, DilloImgType type)
{
DICacheEntry *DicEntry;
- size_t Size = width * height * 3;
+ _MSG("a_Dicache_set_parms (%s)\n", URL_STR(url));
dReturn_if_fail ( Image != NULL && width && height );
/* Find the DicEntry for this Image */
- DicEntry = Dicache_get_entry_version(url, version);
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
+ /* Parameters already set? */
+ dReturn_if_fail ( DicEntry->State < DIC_SetParms );
- /* Initialize the DicEntry */
- DicEntry->linebuf = dNew(uchar_t, width * 3);
- dReturn_if_fail ( DicEntry->linebuf != NULL );
+ _MSG(" RefCount=%d version=%d\n", DicEntry->RefCount, DicEntry->version);
/* BUG: there's just one image-type now */
#define I_RGB 0
- DicEntry->v_imgbuf = a_Image_imgbuf_new(Image->dw, I_RGB, width, height);
+ DicEntry->v_imgbuf = a_Imgbuf_new(Image->dw, I_RGB, width, height);
- /* This extra reference activates the dicache ALWAYS.
- * Extra code is necessary in Imgbuf to be able to free it */
- //a_Image_imgbuf_ref(DicEntry->v_imgbuf);
-
- DicEntry->TotalSize = Size;
+ DicEntry->TotalSize = width * height * 3;
DicEntry->width = width;
DicEntry->height = height;
DicEntry->type = type;
DicEntry->BitVec = a_Bitvec_new((int)height);
DicEntry->State = DIC_SetParms;
- dicache_size_total += Size;
-
- /* Allocate and initialize this image */
- a_Image_set_parms(Image, DicEntry->v_imgbuf, url, version,
- width, height, type);
+ dicache_size_total += DicEntry->TotalSize;
}
/*
@@ -349,8 +295,9 @@ void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index)
{
- DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
+ DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
+ _MSG("a_Dicache_set_cmap\n");
dReturn_if_fail ( DicEntry != NULL );
dFree(DicEntry->cmap);
@@ -362,24 +309,27 @@ void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
DicEntry->cmap[bg_index * 3 + 2] = (Image->bg_color) & 0xff;
}
- a_Image_set_cmap(Image, DicEntry->cmap);
DicEntry->State = DIC_SetCmap;
}
/*
* Reset for a new scan from a multiple-scan image.
*/
-void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version)
+void a_Dicache_new_scan(const DilloUrl *url, int version)
{
DICacheEntry *DicEntry;
+ _MSG("a_Dicache_new_scan\n");
dReturn_if_fail ( url != NULL );
- DicEntry = Dicache_get_entry_version(url, version);
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
-
+ if (DicEntry->State < DIC_SetParms) {
+ MSG("a_Dicache_new_scan before DIC_SetParms\n");
+ exit(1);
+ }
a_Bitvec_clear(DicEntry->BitVec);
DicEntry->ScanNumber++;
- a_Image_new_scan(image, DicEntry->v_imgbuf);
+ a_Imgbuf_new_scan(DicEntry->v_imgbuf);
}
/*
@@ -388,18 +338,19 @@ void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version)
* buf: row buffer
* Y : row number
*/
-void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
- const uchar_t *buf, uint_t Y)
+void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y)
{
DICacheEntry *DicEntry;
- dReturn_if_fail ( Image != NULL );
- DicEntry = Dicache_get_entry_version(url, version);
+ _MSG("a_Dicache_write\n");
+ DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
dReturn_if_fail ( DicEntry->width > 0 && DicEntry->height > 0 );
- a_Image_write(Image, DicEntry->v_imgbuf, buf, Y, TRUE);
- DicEntry->Y = Y;
+ /* update the common buffer in the imgbuf */
+ a_Imgbuf_update(DicEntry->v_imgbuf, buf, DicEntry->type,
+ DicEntry->cmap, DicEntry->width, DicEntry->height, Y);
+
a_Bitvec_set_bit(DicEntry->BitVec, (int)Y);
DicEntry->State = DIC_Write;
}
@@ -410,19 +361,169 @@ void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client)
{
DilloWeb *Web = Client->Web;
- DICacheEntry *DicEntry = Dicache_get_entry_version(url, version);
+ DICacheEntry *DicEntry = a_Dicache_get_entry(url, version);
dReturn_if_fail ( DicEntry != NULL );
- DicEntry->State = DIC_Close;
- dFree(DicEntry->cmap);
- DicEntry->cmap = NULL;
- dFree(DicEntry->linebuf);
- DicEntry->linebuf = NULL;
- a_Image_close(Web->Image);
+ /* a_Dicache_unref() may free DicEntry */
+ _MSG("a_Dicache_close RefCount=%d\n", DicEntry->RefCount - 1);
+
+ if (DicEntry->State < DIC_Close) {
+ DicEntry->State = DIC_Close;
+ dFree(DicEntry->cmap);
+ DicEntry->cmap = NULL;
+ DicEntry->Decoder = NULL;
+ DicEntry->DecoderData = NULL;
+ }
+ a_Dicache_unref(url, version);
+
a_Bw_close_client(Web->bw, Client->Key);
}
+/* ------------------------------------------------------------------------- */
+
+/*
+ * Generic MIME handler for GIF, JPEG and PNG.
+ * Sets a_Dicache_callback as the cache-client,
+ * and also sets the image decoder.
+ *
+ * Parameters:
+ * Type: MIME type
+ * Ptr: points to a Web structure
+ * Call: Dillo calls this with more data/eod
+ * Data: Decoding data structure
+ */
+static void *Dicache_image(int ImgType, const char *MimeType, void *Ptr,
+ CA_Callback_t *Call, void **Data)
+{
+ DilloWeb *web = Ptr;
+ DICacheEntry *DicEntry;
+
+ dReturn_val_if_fail(MimeType && Ptr, NULL);
+
+ if (!web->Image) {
+ web->Image = a_Image_new(NULL, web->bgColor);
+ a_Image_ref(web->Image);
+ }
+
+ DicEntry = a_Dicache_get_entry(web->url, DIC_Last);
+ if (!DicEntry) {
+ /* Let's create an entry for this image... */
+ DicEntry = Dicache_add_entry(web->url);
+ DicEntry->DecoderData =
+ (ImgType == DIC_Png) ?
+ a_Png_new(web->Image, DicEntry->url, DicEntry->version) :
+ (ImgType == DIC_Gif) ?
+ a_Gif_new(web->Image, DicEntry->url, DicEntry->version) :
+ (ImgType == DIC_Jpeg) ?
+ a_Jpeg_new(web->Image, DicEntry->url, DicEntry->version) :
+ NULL;
+ } else {
+ /* Repeated image */
+ a_Dicache_ref(DicEntry->url, DicEntry->version);
+ }
+ DicEntry->Decoder = (ImgType == DIC_Png) ? (CA_Callback_t)a_Png_callback :
+ (ImgType == DIC_Gif) ? (CA_Callback_t)a_Gif_callback :
+ (ImgType == DIC_Jpeg) ? (CA_Callback_t)a_Jpeg_callback:
+ NULL;
+ *Data = DicEntry->DecoderData;
+ *Call = (CA_Callback_t) a_Dicache_callback;
+
+ return (web->Image->dw);
+}
+
+/*
+ * PNG wrapper for Dicache_image()
+ */
+void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Png, Type, Ptr, Call, Data);
+}
+
+/*
+ * GIF wrapper for Dicache_image()
+ */
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Gif, Type, Ptr, Call, Data);
+}
+
+/*
+ * JPEG wrapper for Dicache_image()
+ */
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data)
+{
+ return Dicache_image(DIC_Jpeg, Type, Ptr, Call, Data);
+}
+
+/*
+ * This function is a cache client; (but feeds its clients from dicache)
+ */
+void a_Dicache_callback(int Op, CacheClient_t *Client)
+{
+ uint_t i;
+ DilloWeb *Web = Client->Web;
+ DilloImage *Image = Web->Image;
+ DICacheEntry *DicEntry = a_Dicache_get_entry(Web->url, DIC_Last);
+
+ dReturn_if_fail ( DicEntry != NULL );
+
+ /* Copy the version number in the Client */
+ if (Client->Version == 0)
+ Client->Version = DicEntry->version;
+
+ /* Only call the decoder when necessary */
+ if (Op == CA_Send && DicEntry->State < DIC_Close &&
+ DicEntry->DecodedSize < Client->BufSize) {
+ DicEntry->Decoder(Op, Client);
+ DicEntry->DecodedSize = Client->BufSize;
+ } else if (Op == CA_Close || Op == CA_Abort) {
+ if (DicEntry->State < DIC_Close) {
+ DicEntry->Decoder(Op, Client);
+ } else {
+ a_Dicache_close(DicEntry->url, DicEntry->version, Client);
+ }
+ }
+
+ /* when the data stream is not an image 'v_imgbuf' remains NULL */
+ if (Op == CA_Send && DicEntry->v_imgbuf) {
+ if (Image->height == 0 && DicEntry->State >= DIC_SetParms) {
+ /* Set parms */
+ a_Image_set_parms(
+ Image, DicEntry->v_imgbuf, DicEntry->url,
+ DicEntry->version, DicEntry->width, DicEntry->height,
+ DicEntry->type);
+ }
+ if (DicEntry->State == DIC_Write) {
+ if (DicEntry->ScanNumber == Image->ScanNumber) {
+ for (i = 0; i < DicEntry->height; ++i)
+ if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) &&
+ !a_Bitvec_get_bit(Image->BitVec, (int)i) )
+ a_Image_write(Image, i);
+ } else {
+ for (i = 0; i < DicEntry->height; ++i) {
+ if (a_Bitvec_get_bit(DicEntry->BitVec, (int)i) ||
+ !a_Bitvec_get_bit(Image->BitVec, (int)i) ||
+ DicEntry->ScanNumber > Image->ScanNumber + 1) {
+ a_Image_write(Image, i);
+ }
+ if (!a_Bitvec_get_bit(DicEntry->BitVec, (int)i))
+ a_Bitvec_clear_bit(Image->BitVec, (int)i);
+ }
+ Image->ScanNumber = DicEntry->ScanNumber;
+ }
+ }
+ } else if (Op == CA_Close || Op == CA_Abort) {
+ a_Image_close(Image);
+ a_Bw_close_client(Web->bw, Client->Key);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
/*
* Free the imgbuf (RGB data) of unused entries.
*/
@@ -432,12 +533,13 @@ void a_Dicache_cleanup(void)
DICacheNode *node;
DICacheEntry *entry;
+ _MSG("a_Dicache_cleanup\n");
for (i = 0; i < dList_length(CachedIMGs); ++i) {
node = dList_nth_data(CachedIMGs, i);
/* iterate each entry of this node */
for (entry = node->first; entry; entry = entry->next) {
if (entry->v_imgbuf &&
- a_Image_imgbuf_last_reference(entry->v_imgbuf)) {
+ a_Imgbuf_last_reference(entry->v_imgbuf)) {
/* free this unused entry */
if (entry->next) {
Dicache_remove(node->url, entry->version);
@@ -467,12 +569,11 @@ void a_Dicache_freeall(void)
while ((entry = node->first)) {
node->first = entry->next;
dFree(entry->cmap);
- dFree(entry->linebuf);
a_Bitvec_free(entry->BitVec);
- a_Image_imgbuf_unref(entry->v_imgbuf);
+ a_Imgbuf_unref(entry->v_imgbuf);
dicache_size_total -= entry->TotalSize;
}
- dList_remove(CachedIMGs, node);
+ dList_remove_fast(CachedIMGs, node);
a_Url_free(node->url);
dFree(node);
}
diff --git a/src/dicache.h b/src/dicache.h
index 6cbcf3a5..70adb6c0 100644
--- a/src/dicache.h
+++ b/src/dicache.h
@@ -10,6 +10,10 @@ extern "C" {
#include "image.hh"
#include "cache.h"
+/* Symbolic name to request the last version of an image */
+#define DIC_Last -1
+
+
/* These will reflect the entry's "state" */
typedef enum {
DIC_Empty, /* Just created the entry */
@@ -27,10 +31,8 @@ struct _DICacheEntry {
uint_t width, height; /* As taken from image data */
DilloImgType type; /* Image type */
uchar_t *cmap; /* Color map */
- uchar_t *linebuf; /* Decompressed RGB buffer for one line */
void *v_imgbuf; /* Void pointer to an Imgbuf object */
- size_t TotalSize; /* Amount of memory the image takes up */
- int Y; /* Current decoding row */
+ uint_t TotalSize; /* Amount of memory the image takes up */
uint_t ScanNumber; /* Current decoding scan */
bitvec_t *BitVec; /* Bit vector for decoded rows */
DicEntryState State; /* Current status for this entry */
@@ -38,15 +40,24 @@ struct _DICacheEntry {
int version; /* Version number, used for different
versions of the same URL image */
+ CA_Callback_t Decoder; /* Client function */
+ void *DecoderData; /* Client function data */
+ uint_t DecodedSize; /* Size of already decoded data */
+
DICacheEntry *next; /* Link to the next "newer" version */
};
void a_Dicache_init (void);
-DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url);
-DICacheEntry *a_Dicache_add_entry(const DilloUrl *Url);
+DICacheEntry *a_Dicache_get_entry(const DilloUrl *Url, int version);
+void *a_Dicache_png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
+void *a_Dicache_jpeg_image(const char *Type, void *Ptr, CA_Callback_t *Call,
+ void **Data);
void a_Dicache_callback(int Op, CacheClient_t *Client);
void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
@@ -54,9 +65,8 @@ void a_Dicache_set_parms(DilloUrl *url, int version, DilloImage *Image,
void a_Dicache_set_cmap(DilloUrl *url, int version, DilloImage *Image,
const uchar_t *cmap, uint_t num_colors,
int num_colors_max, int bg_index);
-void a_Dicache_new_scan(DilloImage *image, const DilloUrl *url, int version);
-void a_Dicache_write(DilloImage *Image, DilloUrl *url, int version,
- const uchar_t *buf, uint_t Y);
+void a_Dicache_new_scan(const DilloUrl *url, int version);
+void a_Dicache_write(DilloUrl *url, int version, const uchar_t *buf, uint_t Y);
void a_Dicache_close(DilloUrl *url, int version, CacheClient_t *Client);
void a_Dicache_invalidate_entry(const DilloUrl *Url);
diff --git a/src/dillo.cc b/src/dillo.cc
index 7e29ac32..a42b0dab 100644
--- a/src/dillo.cc
+++ b/src/dillo.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
@@ -26,16 +25,19 @@
#include <fltk/Window.h>
#include <fltk/TabGroup.h>
+#include <fltk/Font.h>
#include <fltk/run.h>
#include "msg.h"
-#include "dir.h"
+#include "paths.hh"
#include "uicmd.hh"
#include "prefs.h"
+#include "prefsparser.hh"
+#include "keys.hh"
#include "bw.h"
#include "misc.h"
-#include "nav.h"
+#include "history.h"
#include "dns.h"
#include "web.hh"
@@ -44,37 +46,174 @@
#include "capi.h"
#include "dicache.h"
#include "cookies.h"
+#include "auth.h"
+/*
+ * Command line options structure
+ */
+typedef enum {
+ DILLO_CLI_NONE = 0,
+ DILLO_CLI_XID = 1 << 0,
+ DILLO_CLI_FULLWINDOW = 1 << 1,
+ DILLO_CLI_HELP = 1 << 2,
+ DILLO_CLI_VERSION = 1 << 3,
+ DILLO_CLI_LOCAL = 1 << 4,
+ DILLO_CLI_GEOMETRY = 1 << 5,
+ DILLO_CLI_ERROR = 1 << 15,
+} OptID;
+
+typedef struct {
+ const char *shortopt;
+ const char *longopt;
+ int opt_argc; /* positive: mandatory, negative: optional */
+ OptID id;
+ const char *help;
+} CLI_options;
+
+static const CLI_options Options[] = {
+ {"-f", "--fullwindow", 0, DILLO_CLI_FULLWINDOW,
+ " -f, --fullwindow Start in full window mode: hide address bar,\n"
+ " navigation buttons, menu, and status bar."},
+ {"-g", "-geometry", 1, DILLO_CLI_GEOMETRY,
+ " -g, -geometry GEO Set initial window position where GEO is\n"
+ " WxH[{+-}X{+-}Y]"},
+ {"-h", "--help", 0, DILLO_CLI_HELP,
+ " -h, --help Display this help text and exit."},
+ {"-l", "--local", 0, DILLO_CLI_LOCAL,
+ " -l, --local Don't load images for these URL(s)."},
+ {"-v", "--version", 0, DILLO_CLI_VERSION,
+ " -v, --version Display version info and exit."},
+ {"-x", "--xid", 1, DILLO_CLI_XID,
+ " -x, --xid XID Open first Dillo window in an existing\n"
+ " window whose window ID is XID."},
+ {NULL, NULL, 0, DILLO_CLI_NONE, NULL}
+};
+
+/*
+ * Print help text generated from the options structure
+ */
+static void printHelp(const char *cmdname, const CLI_options *options)
+{
+ printf("Usage: %s [OPTION]... [--] [URL|FILE]...\n"
+ "Options:\n", cmdname);
+ while (options && options->help) {
+ printf("%s\n", options->help);
+ options++;
+ }
+ printf(" URL URL to browse.\n"
+ " FILE Local FILE to view.\n"
+ "\n");
+}
+
+/*
+ * Return the maximum number of option arguments
+ */
+static int numOptions(const CLI_options *options)
+{
+ int i, max;
+
+ for (i = 0, max = 0; options[i].shortopt; i++)
+ if (abs(options[i].opt_argc) > max)
+ max = abs(options[i].opt_argc);
+ return max;
+}
+
+/*
+ * Get next command line option.
+ */
+static OptID getCmdOption(const CLI_options *options, int argc, char **argv,
+ char **opt_argv, int *idx)
+{
+ typedef enum { O_SEARCH, O_FOUND, O_NOTFOUND, O_DONE } State;
+ OptID opt_id = DILLO_CLI_NONE;
+ int i = 0;
+ State state = O_SEARCH;
+
+ if (*idx >= argc) {
+ state = O_DONE;
+ } else {
+ state = O_NOTFOUND;
+ for (i = 0; options[i].shortopt; i++) {
+ if (strcmp(options[i].shortopt, argv[*idx]) == 0 ||
+ strcmp(options[i].longopt, argv[*idx]) == 0) {
+ state = O_FOUND;
+ ++*idx;
+ break;
+ }
+ }
+ }
+ if (state == O_FOUND) {
+ int n_arg = options[i].opt_argc;
+ opt_id = options[i].id;
+ /* Find the required/optional arguments of the option */
+ for (i = 0; *idx < argc && i < abs(n_arg) && argv[*idx][0] != '-'; i++)
+ opt_argv[i] = argv[(*idx)++];
+ opt_argv[i] = NULL;
+
+ /* Optional arguments have opt_argc < 0 */
+ if (i < n_arg) {
+ fprintf(stderr, "Option %s requires %d argument%s\n",
+ argv[*idx-i-1], n_arg, (n_arg == 1) ? "" : "s");
+ opt_id = DILLO_CLI_ERROR;
+ }
+ }
+ if (state == O_NOTFOUND) {
+ if (strcmp(argv[*idx], "--") == 0)
+ (*idx)++;
+ else if (argv[*idx][0] == '-') {
+ fprintf(stderr, "Command line option \"%s\" not recognized.\n",
+ argv[*idx]);
+ opt_id = DILLO_CLI_ERROR;
+ }
+ }
+ return opt_id;
+}
+
+/*
+ * Tell the user if default/pref fonts can't be found.
+ */
+static void checkFont(const char *name, const char *type)
+{
+ if (::fltk::font(name) == NULL)
+ MSG_WARN("preferred %s font \"%s\" not found.\n", type, name);
+}
+
+static void checkPreferredFonts()
+{
+ checkFont(prefs.font_sans_serif, "sans-serif");
+ checkFont(prefs.font_serif, "serif");
+ checkFont(prefs.font_monospace, "monospace");
+ checkFont(prefs.font_cursive, "cursive");
+ checkFont(prefs.font_fantasy, "fantasy");
+}
/*
* Given a command line argument, build a DilloUrl for it.
*/
-static DilloUrl *Dillo_make_start_url(char *str)
+static DilloUrl *makeStartUrl(char *str, bool local)
{
char *url_str, *p;
DilloUrl *start_url;
- int is_file = FALSE;
/* Relative path to a local file? */
- p = (*str == '/') ? dStrdup(str) : dStrconcat(a_Dir_get_owd(),"/",str,NULL);
+ p = (*str == '/') ? dStrdup(str) :
+ dStrconcat(Paths::getOldWorkingDir(), "/", str, NULL);
if (access(p, F_OK) == 0) {
/* absolute path may have non-URL characters */
url_str = a_Misc_escape_chars(p, "% ");
- is_file = TRUE;
+ start_url = a_Url_new(url_str + 1, "file:/");
} else {
/* Not a file, filter URL string */
url_str = a_Url_string_strip_delimiters(str);
- }
- dFree(p);
-
- if (is_file) {
- start_url = a_Url_new(url_str + 1, "file:/");
- } else {
start_url = a_Url_new(url_str, NULL);
}
+ dFree(p);
dFree(url_str);
+ if (local)
+ a_Url_set_flags(start_url, URL_FLAGS(start_url) | URL_SpamSafe);
+
return start_url;
}
@@ -83,15 +222,79 @@ static DilloUrl *Dillo_make_start_url(char *str)
*/
int main(int argc, char **argv)
{
+ uint_t opt_id;
+ uint_t options_got = 0;
+ uint32_t xid = 0;
+ int idx = 1;
+ int xpos = PREFS_GEOMETRY_DEFAULT_XPOS, ypos = PREFS_GEOMETRY_DEFAULT_YPOS,
+ width = PREFS_GEOMETRY_DEFAULT_WIDTH,
+ height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
+ char **opt_argv;
+ FILE *fp;
+
srand((uint_t)(time(0) ^ getpid()));
// Some OSes exit dillo without this (not GNU/Linux).
signal(SIGPIPE, SIG_IGN);
- // Initialize internal modules
- a_Dir_init();
+ /* Handle command line options */
+ opt_argv = dNew0(char*, numOptions(Options) + 1);
+ while ((opt_id = getCmdOption(Options, argc, argv, opt_argv, &idx))) {
+ options_got |= opt_id;
+ switch (opt_id) {
+ case DILLO_CLI_FULLWINDOW:
+ case DILLO_CLI_LOCAL:
+ break;
+ case DILLO_CLI_XID:
+ {
+ char *end;
+ xid = strtol(opt_argv[0], &end, 0);
+ if (*end) {
+ fprintf(stderr, "XID argument \"%s\" not valid.\n",opt_argv[0]);
+ return 2;
+ }
+ break;
+ }
+ case DILLO_CLI_GEOMETRY:
+ if (!a_Misc_parse_geometry(opt_argv[0],&xpos,&ypos,&width,&height)){
+ fprintf(stderr, "geometry argument \"%s\" not valid. Must be of "
+ "the form WxH[{+-}X{+-}Y].\n", opt_argv[0]);
+ return 2;
+ }
+ break;
+ case DILLO_CLI_VERSION:
+ puts("Dillo version " VERSION);
+ return 0;
+ case DILLO_CLI_HELP:
+ printHelp(argv[0], Options);
+ return 0;
+ default:
+ printHelp(argv[0], Options);
+ return 2;
+ }
+ }
+ dFree(opt_argv);
+
+ // set the default values for the preferences
a_Prefs_init();
- a_Dir_check_dillorc_directory(); /* and create if not present */
+
+ // create ~/.dillo if not present
+ Paths::init();
+
+ // initialize default key bindings
+ Keys::init();
+
+ // parse dillorc
+ if ((fp = Paths::getPrefsFP(PATHS_RC_PREFS))) {
+ PrefsParser::parse(fp);
+ }
+ // parse keysrc
+ if ((fp = Paths::getPrefsFP(PATHS_RC_KEYS))) {
+ Keys::parse(fp);
+ }
+ dLib_show_messages(prefs.show_msg);
+
+ // initialize internal modules
a_Dpi_init();
a_Dns_init();
a_Web_init();
@@ -101,6 +304,17 @@ int main(int argc, char **argv)
a_Dicache_init();
a_Bw_init();
a_Cookies_init();
+ a_Auth_init();
+
+ /* command line options override preferences */
+ if (options_got & DILLO_CLI_FULLWINDOW)
+ prefs.fullwindow_start = TRUE;
+ if (options_got & DILLO_CLI_GEOMETRY) {
+ prefs.width = width;
+ prefs.height = height;
+ prefs.xpos = xpos;
+ prefs.ypos = ypos;
+ }
// Sets WM_CLASS hint on X11
fltk::Window::xclass("dillo");
@@ -108,9 +322,18 @@ int main(int argc, char **argv)
// WORKAROUND: sometimes the default pager triggers redraw storms
fltk::TabGroup::default_pager(fltk::PAGER_SHRINK);
+ checkPreferredFonts();
+ /* use preferred font for UI */
+ fltk::Font *dfont = fltk::font(prefs.font_sans_serif, 0);
+ if (dfont) {
+ fltk::Widget::default_style->textfont(dfont);
+ fltk::Widget::default_style->labelfont(dfont);
+ }
+
// Create a new UI/bw pair
- BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, NULL);
+ BrowserWindow *bw = a_UIcmd_browser_window_new(0, 0, xid, NULL);
+ /* Proxy authentication */
if (prefs.http_proxyuser && !a_Http_proxy_auth()) {
const char *passwd = a_UIcmd_get_passwd(prefs.http_proxyuser);
if (passwd) {
@@ -120,24 +343,50 @@ int main(int argc, char **argv)
}
}
- if (argc == 2) {
- DilloUrl *url = Dillo_make_start_url(argv[1]);
- a_UIcmd_open_urlstr(bw, URL_STR(url));
- a_Url_free(url);
- } else if (argc == 6) {
- // WORKAROUND: sylpheed execs "dillo -l -f -x XID URL"
- if (strcmp(argv[1], "-l") == 0 && strcmp(argv[2], "-f") == 0 &&
- strcmp(argv[3], "-x") == 0) {
- a_UIcmd_set_images_enabled(bw, FALSE);
- DilloUrl *url = Dillo_make_start_url(argv[5]);
- a_Url_set_flags(url, URL_FLAGS(url) & URL_SpamSafe);
- a_UIcmd_open_urlstr(bw, URL_STR(url));
- a_Url_free(url);
- }
+ /* Open URLs/files */
+ const bool local = options_got & DILLO_CLI_LOCAL;
+
+ if (idx == argc) {
+ /* No URLs/files on cmdline. Send startup screen */
+ a_UIcmd_open_url(bw, prefs.start_page);
} else {
- /* Send startup screen */
- a_Nav_push(bw, prefs.start_page);
+ for (int i = idx; i < argc; i++) {
+ DilloUrl *start_url = makeStartUrl(argv[i], local);
+
+ if (i > idx) {
+ if (prefs.middle_click_opens_new_tab) {
+ /* user must prefer tabs */
+ const int focus = 1;
+ a_UIcmd_open_url_nt(bw, start_url, focus);
+ } else {
+ a_UIcmd_open_url_nw(bw, start_url);
+ }
+ } else {
+ a_UIcmd_open_url(bw, start_url);
+ }
+ a_Url_free(start_url);
+ }
}
- return fltk::run();
+ fltk::run();
+
+ /*
+ * Memory deallocating routines
+ * (This can be left to the OS, but we'll do it, with a view to test
+ * and fix our memory management)
+ */
+ a_Cookies_freeall();
+ a_Cache_freeall();
+ a_Dicache_freeall();
+ a_Http_freeall();
+ a_Dns_freeall();
+ a_History_freeall();
+ a_Prefs_freeall();
+ Keys::free();
+ Paths::free();
+ /* TODO: auth, css */
+
+ //a_Dpi_dillo_exit();
+ MSG("Dillo: normal exit!\n");
+ return 0;
}
diff --git a/src/dir.c b/src/dir.c
deleted file mode 100644
index f7b2644d..00000000
--- a/src/dir.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * File: dir.c
- *
- * Copyright 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- */
-
-#include <unistd.h>
-#include <errno.h>
-#include <sys/stat.h>
-
-#include "msg.h"
-#include "../dlib/dlib.h"
-
-
-/*
- * Local data
- */
-/* Dillo works from an unmounted directory (/tmp). */
-static char *OldWorkingDirectory = NULL;
-
-/*
- * Change current working directory to "/tmp".
- */
-void a_Dir_init(void)
-{
- dFree(OldWorkingDirectory);
- OldWorkingDirectory = dGetcwd();
- chdir("/tmp");
-}
-
-/*
- * Return the initial current working directory in a string.
- */
-char *a_Dir_get_owd(void)
-{
- return OldWorkingDirectory;
-}
-
-/*
- * Free memory
- */
-void a_Dir_free(void)
-{
- dFree(OldWorkingDirectory);
-}
-
-/*
- * Check if '~/.dillo' directory exists.
- * If not, try to create it.
- */
-void a_Dir_check_dillorc_directory(void)
-{
- char *dir;
- struct stat st;
-
- dir = dStrconcat(dGethomedir(), "/.dillo", NULL);
- if (stat(dir, &st) == -1) {
- if (errno == ENOENT) {
- MSG("Dillo: creating directory %s.\n", dir);
- if (mkdir(dir, 0700) < 0) {
- MSG("Dillo: error creating directory %s: %s\n", dir,
- dStrerror(errno));
- }
- } else {
- MSG("Dillo: error reading %s: %s\n", dir, dStrerror(errno));
- }
- }
- dFree(dir);
-}
-
diff --git a/src/dir.h b/src/dir.h
deleted file mode 100644
index 19d69713..00000000
--- a/src/dir.h
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef __DIR_H__
-#define __DIR_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-
-void a_Dir_init(void);
-char *a_Dir_get_owd(void);
-void a_Dir_free(void);
-void a_Dir_check_dillorc_directory(void);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif /* __DIR_H__ */
-
diff --git a/src/djpeg.h b/src/djpeg.h
new file mode 100644
index 00000000..32419e4b
--- /dev/null
+++ b/src/djpeg.h
@@ -0,0 +1,19 @@
+#ifndef __JPEG_H__
+#define __JPEG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Jpeg_callback(int Op, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__JPEG_H__ */
diff --git a/src/dns.c b/src/dns.c
index c783366f..980586c6 100644
--- a/src/dns.c
+++ b/src/dns.c
@@ -13,17 +13,26 @@
* Non blocking pthread-handled Dns scheme
*/
-#include <pthread.h>
+
+/*
+ * Uncomment the following line for debugging or gprof profiling.
+ */
+/* #undef D_DNS_THREADED */
+
+#ifdef D_DNS_THREADED
+# include <pthread.h>
+#endif
+
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <string.h>
#include "msg.h"
@@ -31,11 +40,6 @@
#include "list.h"
#include "timeout.hh"
-/*
- * Uncomment the following line for debugging or gprof profiling.
- */
-/* #undef D_DNS_THREADED */
-
/* Maximum dns resolving threads */
#ifdef D_DNS_THREADED
@@ -84,7 +88,6 @@ static GDnsCache *dns_cache;
static int dns_cache_size, dns_cache_size_max;
static GDnsQueue *dns_queue;
static int dns_queue_size, dns_queue_size_max;
-static bool_t ipv6_enabled;
/* ----------------------------------------------------------------------
@@ -197,16 +200,14 @@ void a_Dns_init(void)
#endif
}
- /* IPv6 test */
- ipv6_enabled = FALSE;
#ifdef ENABLE_IPV6
+ /* IPv6 test */
{
/* If the IPv6 address family is not available there is no point
wasting time trying to connect to v6 addresses. */
int fd = socket(AF_INET6, SOCK_STREAM, 0);
if (fd >= 0) {
close(fd);
- ipv6_enabled = TRUE;
}
}
#endif
@@ -267,12 +268,15 @@ static void *Dns_server(void *data)
int channel = VOIDP2INT(data);
struct addrinfo hints, *res0;
int error;
+ Dlist *hosts;
+ size_t length, i;
+ char addr_string[40];
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- Dlist *hosts = dList_new(2);
+ hosts = dList_new(2);
_MSG("Dns_server: starting...\n ch: %d host: %s\n",
channel, dns_server[channel].hostname);
@@ -306,8 +310,18 @@ static void *Dns_server(void *data)
}
/* tell our findings */
- MSG("Dns_server [%d]: %s is %p\n", channel,
- dns_server[channel].hostname, hosts);
+ MSG("Dns_server [%d]: %s is", channel,
+ dns_server[channel].hostname);
+ if ((length = dList_length(hosts))) {
+ for (i = 0; i < length; i++) {
+ a_Dns_dillohost_to_string(dList_nth_data(hosts, i),
+ addr_string, sizeof(addr_string));
+ MSG(" %s", addr_string);
+ }
+ MSG("\n");
+ } else {
+ MSG(" (nil)\n");
+ }
dns_server[channel].addr_list = hosts;
dns_server[channel].ip_ready = TRUE;
@@ -367,7 +381,7 @@ void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data)
break;
if (i < dns_cache_size) {
- /* already resolved, call the Callback inmediately. */
+ /* already resolved, call the Callback immediately. */
cb_func(0, dns_cache[i].addr_list, cb_data);
} else if ((i = Dns_queue_find(hostname)) != -1) {
@@ -469,15 +483,35 @@ static void Dns_timeout_client(void *data)
* Dns memory-deallocation
* (Call this one at exit time)
* The Dns_queue is deallocated at execution time (no need to do that here)
- * 'dns_cache' is the only one that grows dinamically
+ * 'dns_cache' is the only one that grows dynamically
*/
void a_Dns_freeall(void)
{
- int i;
+ int i, j;
for ( i = 0; i < dns_cache_size; ++i ){
dFree(dns_cache[i].hostname);
+ for ( j = 0; j < dList_length(dns_cache[i].addr_list); ++j)
+ dFree(dList_nth_data(dns_cache[i].addr_list, j));
+ dList_free(dns_cache[i].addr_list);
}
dFree(dns_cache);
}
+/*
+ * Writes a string representation of the given DilloHost
+ * into dst. dst will be \0 terminated.
+ * Please note that dst must be at least 40 bytes long for IPv6
+ * addresses.
+ */
+void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size)
+{
+ if (!inet_ntop(host->af, host->data, dst, size)) {
+ switch (errno) {
+ case EAFNOSUPPORT:
+ snprintf(dst, size, "Unknown address family");
+ case ENOSPC:
+ snprintf(dst, size, "Buffer too small");
+ }
+ }
+}
diff --git a/src/dns.h b/src/dns.h
index 1749044f..78f8cf18 100644
--- a/src/dns.h
+++ b/src/dns.h
@@ -14,7 +14,11 @@ void a_Dns_init (void);
void a_Dns_freeall(void);
void a_Dns_resolve(const char *hostname, DnsCallback_t cb_func, void *cb_data);
-#define DILLO_ADDR_MAX sizeof(struct in6_addr)
+#ifdef ENABLE_IPV6
+# define DILLO_ADDR_MAX sizeof(struct in6_addr)
+#else
+# define DILLO_ADDR_MAX sizeof(struct in_addr)
+#endif
typedef struct _DilloHost
{
@@ -22,7 +26,7 @@ typedef struct _DilloHost
int alen;
char data[DILLO_ADDR_MAX];
} DilloHost;
-
+void a_Dns_dillohost_to_string(DilloHost *host, char *dst, size_t size);
#ifdef __cplusplus
}
diff --git a/src/doctree.hh b/src/doctree.hh
new file mode 100644
index 00000000..ef7faa7c
--- /dev/null
+++ b/src/doctree.hh
@@ -0,0 +1,72 @@
+#ifndef __DOCTREE_HH__
+#define __DOCTREE_HH__
+
+#include "lout/misc.hh"
+
+class DoctreeNode {
+ public:
+ DoctreeNode *parent;
+ int num; // unique ascending id
+ int element;
+ lout::misc::SimpleVector<char*> *klass;
+ const char *pseudo;
+ const char *id;
+
+ DoctreeNode () {
+ parent = NULL;
+ klass = NULL;
+ pseudo = NULL;
+ id = NULL;
+ element = 0;
+ };
+};
+
+/**
+ * \brief HTML document tree interface.
+ *
+ * The Doctree class defines the interface to the parsed HTML document tree
+ * as it is used for CSS selector matching.
+ * Currently the Doctree can be represented as stack, however to support
+ * CSS adjacent siblings or for future JavaScript support it may have to
+ * be extended to a real tree.
+ */
+class Doctree {
+ private:
+ DoctreeNode *topNode;
+ int num;
+
+ public:
+ Doctree () {
+ topNode = NULL;
+ num = 0;
+ };
+ ~Doctree () { while (top ()) pop (); };
+ DoctreeNode *push () {
+ DoctreeNode *dn = new DoctreeNode ();
+ dn->parent = topNode;
+ dn->num = num++;
+ topNode = dn;
+ return dn;
+ };
+ void pop () {
+ DoctreeNode *dn = topNode;
+ if (dn) {
+ dFree ((void*) dn->id);
+ if (dn->klass) {
+ for (int i = 0; i < dn->klass->size (); i++)
+ dFree (dn->klass->get(i));
+ delete dn->klass;
+ }
+ topNode = dn->parent;
+ delete dn;
+ }
+ };
+ inline DoctreeNode *top () {
+ return topNode;
+ };
+ inline DoctreeNode *parent (const DoctreeNode *node) {
+ return node->parent;
+ };
+};
+
+#endif
diff --git a/src/dpiapi.c b/src/dpiapi.c
index cf32ed3f..2068146c 100644
--- a/src/dpiapi.c
+++ b/src/dpiapi.c
@@ -41,6 +41,7 @@ static void Dpiapi_dialog_answer_cb(BrowserWindow *bw, int answer)
/* Send answer */
a_Capi_dpi_send_cmd(NULL, bw, cmd, dialog_server, 0);
+ dFree(cmd);
}
/*
@@ -60,12 +61,12 @@ void a_Dpiapi_dialog(BrowserWindow *bw, char *server, char *dpip_tag)
/* other options can be parsed the same way */
dpip_tag_len = strlen(dpip_tag);
- question = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "msg");
- alt1 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt1");
- alt2 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt2");
- alt3 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt3");
- alt4 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt4");
- alt5 = a_Dpip_get_attr(dpip_tag, dpip_tag_len, "alt5");
+ question = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "msg");
+ alt1 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt1");
+ alt2 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt2");
+ alt3 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt3");
+ alt4 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt4");
+ alt5 = a_Dpip_get_attr_l(dpip_tag, dpip_tag_len, "alt5");
ret = a_Dialog_choice5(question, alt1, alt2, alt3, alt4, alt5);
/* As choice5 is modal, call the callback function directly. */
diff --git a/src/dpng.h b/src/dpng.h
new file mode 100644
index 00000000..a9ee8820
--- /dev/null
+++ b/src/dpng.h
@@ -0,0 +1,19 @@
+#ifndef __PNG_H__
+#define __PNG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "url.h"
+#include "image.hh"
+
+
+void *a_Png_new(DilloImage *Image, DilloUrl *url, int version);
+void a_Png_callback(int Op, CacheClient_t *Client);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* !__PNG_H__ */
diff --git a/src/findbar.cc b/src/findbar.cc
index 5d1c6245..8cca52ba 100644
--- a/src/findbar.cc
+++ b/src/findbar.cc
@@ -18,6 +18,8 @@
#include "uicmd.hh"
#include "bw.h"
+using namespace fltk;
+
/*
* Local sub class
* (Used to handle escape in the findbar, may also avoid some shortcuts).
@@ -63,7 +65,22 @@ void Findbar::search_cb(Widget *, void *vfb)
if (key[0] != '\0')
a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
- key, case_sens);
+ key, case_sens, false);
+}
+
+/*
+ * Find previous occurrence of input key
+ */
+void Findbar::searchBackwards_cb(Widget *, void *vfb)
+{
+ Findbar *fb = (Findbar *)vfb;
+ const char *key = fb->i->text();
+ bool case_sens = fb->check_btn->value();
+
+ if (key[0] != '\0') {
+ a_UIcmd_findtext_search(a_UIcmd_get_bw_by_widget(fb),
+ key, case_sens, true);
+ }
}
/*
@@ -74,7 +91,7 @@ void Findbar::search_cb2(Widget *widget, void *vfb)
/*
* Somehow fltk even regards the first loss of focus for the
* window as a WHEN_ENTER_KEY_ALWAYS event.
- */
+ */
if (event_key() == ReturnKey)
search_cb(widget, vfb);
}
@@ -96,7 +113,7 @@ Findbar::Findbar(int width, int height) :
int button_width = 70;
int gap = 2;
int border = 2;
- int input_width = width - (2 * border + 3 * (button_width + gap));
+ int input_width = width - (2 * border + 4 * (button_width + gap));
int x = border;
height -= 2 * border;
@@ -107,7 +124,6 @@ Findbar::Findbar(int width, int height) :
hide_btn = new HighlightButton(x, border, 16, height, 0);
hideImg = new xpmImage(new_s_xpm);
hide_btn->image(hideImg);
- hide_btn->tooltip("Hide");
x += 16 + gap;
hide_btn->callback(hide_cb, this);
hide_btn->clear_tab_to_focus();
@@ -121,21 +137,33 @@ Findbar::Findbar(int width, int height) :
i->clear_tab_to_focus();
i->set_click_to_focus();
- // TODO: search previous would be nice
next_btn = new HighlightButton(x, border, button_width, height, "Next");
x += button_width + gap;
- next_btn->tooltip("Find next occurrence of the search phrase");
next_btn->add_shortcut(ReturnKey);
next_btn->add_shortcut(KeypadEnter);
next_btn->callback(search_cb, this);
next_btn->clear_tab_to_focus();
+ prev_btn= new HighlightButton(x, border, button_width, height, "Previous");
+ prev_btn->add_shortcut(SHIFT+ReturnKey);
+ prev_btn->callback(searchBackwards_cb, this);
+ prev_btn->clear_tab_to_focus();
+ x += button_width + gap;
+
check_btn = new CheckButton(x, border, 2*button_width, height,
"Case-sensitive");
check_btn->clear_tab_to_focus();
x += 2 * button_width + gap;
end();
+
+ if (prefs.show_tooltip) {
+ hide_btn->tooltip("Hide");
+ next_btn->tooltip("Find next occurrence of the search phrase\n"
+ "shortcut: Enter");
+ prev_btn->tooltip("Find previous occurrence of the search phrase\n"
+ "shortcut: Shift+Enter");
+ }
}
Findbar::~Findbar()
diff --git a/src/findbar.hh b/src/findbar.hh
index 1e8c8d66..ba7ed8ed 100644
--- a/src/findbar.hh
+++ b/src/findbar.hh
@@ -9,21 +9,20 @@
#include <fltk/Group.h>
#include <fltk/CheckButton.h>
-using namespace fltk;
-
/*
* Searchbar to find text in page.
*/
-class Findbar : public Group {
- Button *clrb;
- HighlightButton *hide_btn, *next_btn;
- CheckButton *check_btn;
- xpmImage *hideImg;
- Input *i;
-
- static void search_cb (Widget *, void *);
- static void search_cb2 (Widget *, void *);
- static void hide_cb (Widget *, void *);
+class Findbar : public fltk::Group {
+ fltk::Button *clrb;
+ fltk::HighlightButton *hide_btn, *next_btn, *prev_btn;
+ fltk::CheckButton *check_btn;
+ fltk::xpmImage *hideImg;
+ fltk::Input *i;
+
+ static void search_cb (fltk::Widget *, void *);
+ static void searchBackwards_cb (fltk::Widget *, void *);
+ static void search_cb2 (fltk::Widget *, void *);
+ static void hide_cb (fltk::Widget *, void *);
public:
Findbar(int width, int height);
diff --git a/src/form.cc b/src/form.cc
index 78bc43e8..38afea8f 100644
--- a/src/form.cc
+++ b/src/form.cc
@@ -22,28 +22,28 @@
#include "misc.h"
#include "msg.h"
#include "prefs.h"
-#include "nav.h"
#include "uicmd.hh"
+using namespace lout;
using namespace dw;
using namespace dw::core;
using namespace dw::core::style;
+using namespace dw::core::ui;
/*
- * Forward declarations
+ * Forward declarations
*/
class DilloHtmlReceiver;
class DilloHtmlSelect;
class DilloHtmlOption;
-static dw::core::ui::Embed *Html_input_image(DilloHtml *html,
- const char *tag, int tagsize);
+static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize);
static void Html_option_finish(DilloHtml *html);
/*
- * Typedefs
+ * Typedefs
*/
typedef enum {
@@ -67,7 +67,7 @@ typedef enum {
} DilloHtmlInputType;
/*
- * Class declarations
+ * Class declarations
*/
class DilloHtmlForm {
@@ -75,74 +75,74 @@ class DilloHtmlForm {
friend class DilloHtmlInput;
DilloHtml *html;
- void eventHandler(dw::core::ui::Resource *resource);
- void submit(DilloHtmlInput *input);
- DilloUrl *buildQueryUrl(DilloHtmlInput *input);
+ bool showing_hiddens;
+ bool enabled;
+ void eventHandler(Resource *resource, EventButton *event);
+ DilloUrl *buildQueryUrl(DilloHtmlInput *active_input);
Dstr *buildQueryData(DilloHtmlInput *active_submit);
- char *makeMultipartBoundary(iconv_t encoder, DilloHtmlInput *active_submit);
- Dstr *encodeText(iconv_t encoder, Dstr **input);
- void urlencodeAppend(Dstr *str, const char *val);
- void appendInputUrlencode(Dstr *data,
+ char *makeMultipartBoundary(iconv_t char_encoder,
+ DilloHtmlInput *active_submit);
+ Dstr *encodeText(iconv_t char_encoder, Dstr **input);
+ void strUrlencodeAppend(Dstr *dstr, const char *str);
+ void inputUrlencodeAppend(Dstr *data, const char *name, const char *value);
+ void inputMultipartAppend(Dstr *data, const char *boundary,
const char *name, const char *value);
- void appendInputMultipartFiles(Dstr* data,
- const char *boundary,
+ void filesInputMultipartAppend(Dstr* data, const char *boundary,
const char *name, Dstr *file,
const char *filename);
- void appendInputMultipart(Dstr *data,
- const char *boundary,
- const char *name,
- const char *value);
- void appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x, Dstr *y);
- void appendClickposMultipart(Dstr *data, const char *boundary, Dstr *name,
- Dstr *x, Dstr *y);
+ void imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x, Dstr *y);
+ void imageInputMultipartAppend(Dstr *data, const char *boundary, Dstr *name,
+ Dstr *x, Dstr *y);
public: //BUG: for now everything is public
DilloHtmlMethod method;
DilloUrl *action;
- DilloHtmlEnc enc;
+ DilloHtmlEnc content_type;
char *submit_charset;
lout::misc::SimpleVector<DilloHtmlInput*> *inputs;
int num_entry_fields;
- int num_submit_buttons;
DilloHtmlReceiver *form_receiver;
public:
- DilloHtmlForm (DilloHtml *html,
+ DilloHtmlForm (DilloHtml *html,
DilloHtmlMethod method, const DilloUrl *action,
- DilloHtmlEnc enc, const char *charset);
+ DilloHtmlEnc content_type, const char *charset,
+ bool enabled);
~DilloHtmlForm ();
- DilloHtmlInput *getInput (dw::core::ui::Resource *resource);
+ DilloHtmlInput *getInput (Resource *resource);
DilloHtmlInput *getRadioInput (const char *name);
+ void submit(DilloHtmlInput *active_input, EventButton *event);
void reset ();
+ void display_hiddens(bool display);
void addInput(DilloHtmlInput *input, DilloHtmlInputType type);
+ void setEnabled(bool enabled);
};
class DilloHtmlReceiver:
- public dw::core::ui::Resource::ActivateReceiver,
- public dw::core::ui::ButtonResource::ClickedReceiver
+ public Resource::ActivateReceiver,
+ public Resource::ClickedReceiver
{
friend class DilloHtmlForm;
DilloHtmlForm* form;
DilloHtmlReceiver (DilloHtmlForm* form2) { form = form2; }
~DilloHtmlReceiver () { }
- void activate (dw::core::ui::Resource *resource);
- void enter (dw::core::ui::Resource *resource);
- void leave (dw::core::ui::Resource *resource);
- void clicked (dw::core::ui::ButtonResource *resource,
- int buttonNo, int x, int y);
+ void activate (Resource *resource);
+ void enter (Resource *resource);
+ void leave (Resource *resource);
+ void clicked (Resource *resource, EventButton *event);
};
class DilloHtmlInput {
- // DilloHtmlForm::addInput() calls connectTo()
+ // DilloHtmlForm::addInput() calls connectTo()
friend class DilloHtmlForm;
public: //BUG: for now everything is public
DilloHtmlInputType type;
- dw::core::ui::Embed *embed; /* May be NULL (think: hidden input) */
+ Embed *embed; /* May be NULL (think: hidden input) */
char *name;
char *init_str; /* note: some overloading - for buttons, init_str
is simply the value of the button; for text
@@ -154,18 +154,16 @@ public: //BUG: for now everything is public
private:
void connectTo(DilloHtmlReceiver *form_receiver);
- void activate(DilloHtmlForm *form, bool force_submit);
+ void activate(DilloHtmlForm *form, int num_entry_fields,EventButton *event);
void readFile(BrowserWindow *bw);
public:
- DilloHtmlInput (DilloHtmlInputType type,
- dw::core::ui::Embed *embed,
- const char *name,
- const char *init_str,
- bool init_val);
+ DilloHtmlInput (DilloHtmlInputType type, Embed *embed,
+ const char *name, const char *init_str, bool init_val);
~DilloHtmlInput ();
void appendValuesTo(Dlist *values, bool is_active_submit);
void reset();
+ void setEnabled(bool enabled) {if (embed) embed->setEnabled(enabled); };
};
class DilloHtmlSelect {
@@ -178,9 +176,8 @@ public:
DilloHtmlOption *getCurrentOption ();
void addOption (char *value, bool selected, bool enabled);
void ensureSelection ();
- void addOptionsTo (dw::core::ui::SelectionResource *res);
- void appendValuesTo (Dlist *values,
- dw::core::ui::SelectionResource *res);
+ void addOptionsTo (SelectionResource *res);
+ void appendValuesTo (Dlist *values, SelectionResource *res);
};
class DilloHtmlOption {
@@ -190,20 +187,20 @@ public:
bool selected, enabled;
private:
DilloHtmlOption (char *value, bool selected, bool enabled);
- ~DilloHtmlOption ();
+ ~DilloHtmlOption ();
};
/*
- * Form API
+ * Form API
*/
-DilloHtmlForm *a_Html_form_new (DilloHtml *html,
- DilloHtmlMethod method,
+DilloHtmlForm *a_Html_form_new (DilloHtml *html, DilloHtmlMethod method,
const DilloUrl *action,
- DilloHtmlEnc enc,
- const char *charset)
+ DilloHtmlEnc content_type, const char *charset,
+ bool enabled)
{
- return new DilloHtmlForm (html,method,action,enc,charset);
+ return new DilloHtmlForm (html, method, action, content_type, charset,
+ enabled);
}
void a_Html_form_delete (DilloHtmlForm *form)
@@ -216,15 +213,30 @@ void a_Html_input_delete (DilloHtmlInput *input)
delete input;
}
+void a_Html_form_submit2(void *vform)
+{
+ ((DilloHtmlForm *)vform)->submit(NULL, NULL);
+}
+
+void a_Html_form_reset2(void *vform)
+{
+ ((DilloHtmlForm *)vform)->reset();
+}
+
+void a_Html_form_display_hiddens2(void *vform, bool display)
+{
+ ((DilloHtmlForm *)vform)->display_hiddens(display);
+}
+
/*
- * Form parsing functions
+ * Form parsing functions
*/
/*
* Add an HTML control
*/
static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
- dw::core::ui::Embed *embed, const char *name,
+ Embed *embed, const char *name,
const char *init_str, bool init_val)
{
_MSG("name=[%s] init_str=[%s] init_val=[%d]\n", name, init_str, init_val);
@@ -236,6 +248,8 @@ static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
int ni = html->inputs_outside_form->size();
html->inputs_outside_form->increase();
html->inputs_outside_form->set(ni, input);
+
+ input->setEnabled(false);
}
}
@@ -244,18 +258,20 @@ static void Html_add_input(DilloHtml *html, DilloHtmlInputType type,
*/
static DilloHtmlInput *Html_get_radio_input(DilloHtml *html, const char *name)
{
- lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
+ if (name) {
+ lout::misc::SimpleVector<DilloHtmlInput*>* inputs;
- if (html->InFlags & IN_FORM)
- inputs = html->getCurrentForm()->inputs;
- else
- inputs = html->inputs_outside_form;
+ if (html->InFlags & IN_FORM)
+ inputs = html->getCurrentForm()->inputs;
+ else
+ inputs = html->inputs_outside_form;
- for (int idx = 0; idx < inputs->size(); idx++) {
- DilloHtmlInput *input = inputs->get(idx);
- if (input->type == DILLO_HTML_INPUT_RADIO &&
- input->name && !dStrcasecmp(input->name, name))
- return input;
+ for (int idx = 0; idx < inputs->size(); idx++) {
+ DilloHtmlInput *input = inputs->get(idx);
+ if (input->type == DILLO_HTML_INPUT_RADIO &&
+ input->name && !dStrcasecmp(input->name, name))
+ return input;
+ }
}
return NULL;
}
@@ -283,11 +299,11 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
{
DilloUrl *action;
DilloHtmlMethod method;
- DilloHtmlEnc enc;
+ DilloHtmlEnc content_type;
char *charset, *first;
const char *attrbuf;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
if (html->InFlags & IN_FORM) {
BUG_MSG("nested forms\n");
@@ -308,13 +324,15 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
}
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "action")))
action = a_Html_url_new(html, attrbuf, NULL, 0);
- else
+ else {
+ BUG_MSG("action attribute required for <form>\n");
action = a_Url_dup(html->base_url);
- enc = DILLO_HTML_ENC_URLENCODING;
+ }
+ content_type = DILLO_HTML_ENC_URLENCODED;
if ((method == DILLO_HTML_METHOD_POST) &&
((attrbuf = a_Html_get_attr(html, tag, tagsize, "enctype")))) {
if (!dStrcasecmp(attrbuf, "multipart/form-data"))
- enc = DILLO_HTML_ENC_MULTIPART;
+ content_type = DILLO_HTML_ENC_MULTIPART;
}
charset = NULL;
first = NULL;
@@ -335,32 +353,19 @@ void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize)
}
if (!charset)
charset = html->charset;
- html->formNew(method, action, enc, charset);
+ html->formNew(method, action, content_type, charset);
dFree(first);
a_Url_free(action);
}
void Html_tag_close_form(DilloHtml *html, int TagIdx)
{
- static const char *SubmitTag =
- "<input type='submit' value='?Submit?' alt='dillo-generated-button'>";
- DilloHtmlForm *form;
+// DilloHtmlForm *form;
// int i;
-
- if (html->InFlags & IN_FORM) {
- form = html->getCurrentForm ();
- /* If we don't have a submit button and the user desires one,
- let's add a custom one */
- if (form->num_submit_buttons == 0) {
- if (prefs.show_extra_warnings || form->num_entry_fields != 1)
- BUG_MSG("FORM lacks a Submit button\n");
- if (prefs.generate_submit) {
- BUG_MSG(" (added a submit button internally)\n");
- Html_tag_open_input(html, SubmitTag, strlen(SubmitTag));
- form->num_submit_buttons = 0;
- }
- }
-
+//
+// if (html->InFlags & IN_FORM) {
+// form = html->getCurrentForm ();
+//
// /* Make buttons sensitive again */
// for (i = 0; i < form->inputs->size(); i++) {
// input_i = form->inputs->get(i);
@@ -376,14 +381,31 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx)
// a_Dw_button_set_sensitive(DW_BUTTON(input_i->widget), TRUE);
// }
// }
- }
+// }
html->InFlags &= ~IN_FORM;
html->InFlags &= ~IN_SELECT;
html->InFlags &= ~IN_OPTION;
html->InFlags &= ~IN_TEXTAREA;
+}
- a_Html_pop_tag(html, TagIdx);
+/*
+ * get size, restrict it to reasonable value
+ */
+static int Html_input_get_size(DilloHtml *html, const char *attrbuf)
+{
+ const int MAX_SIZE = 1024;
+ int size = 20;
+
+ if (attrbuf) {
+ size = strtol(attrbuf, NULL, 10);
+ if (size < 1 || size > MAX_SIZE) {
+ int badSize = size;
+ size = (size < 1 ? 20 : MAX_SIZE);
+ BUG_MSG("input size=%d, using size=%d instead\n", badSize, size);
+ }
+ }
+ return size;
}
/*
@@ -392,11 +414,13 @@ void Html_tag_close_form(DilloHtml *html, int TagIdx)
void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
{
DilloHtmlInputType inp_type;
- dw::core::ui::Embed *embed = NULL;
+ Resource *resource = NULL;
+ Embed *embed = NULL;
char *value, *name, *type, *init_str;
const char *attrbuf, *label;
bool init_val = false;
-
+ ResourceFactory *factory;
+
if (html->InFlags & IN_SELECT) {
BUG_MSG("<input> element inside <select>\n");
return;
@@ -405,59 +429,50 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<input> element inside <button>\n");
return;
}
-
+
+ factory = HT2LT(html)->getResourceFactory();
+
/* Get 'value', 'name' and 'type' */
value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
type = a_Html_get_attr_wdef(html, tag, tagsize, "type", "");
-
+
init_str = NULL;
inp_type = DILLO_HTML_INPUT_UNKNOWN;
if (!dStrcasecmp(type, "password")) {
inp_type = DILLO_HTML_INPUT_PASSWORD;
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, true);
- embed = new dw::core::ui::Embed (entryResource);
- init_str = (value) ? value : NULL;
+ attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
+ int size = Html_input_get_size(html, attrbuf);
+ resource = factory->createEntryResource (size, true, NULL);
+ init_str = value;
} else if (!dStrcasecmp(type, "checkbox")) {
inp_type = DILLO_HTML_INPUT_CHECKBOX;
- dw::core::ui::CheckButtonResource *check_b_r =
- HT2LT(html)->getResourceFactory()->createCheckButtonResource(false);
- embed = new dw::core::ui::Embed (check_b_r);
+ resource = factory->createCheckButtonResource(false);
init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
init_str = (value) ? value : dStrdup("on");
} else if (!dStrcasecmp(type, "radio")) {
inp_type = DILLO_HTML_INPUT_RADIO;
- dw::core::ui::RadioButtonResource *rb_r = NULL;
+ RadioButtonResource *rb_r = NULL;
DilloHtmlInput *input = Html_get_radio_input(html, name);
if (input)
- rb_r =
- (dw::core::ui::RadioButtonResource*)
- input->embed->getResource();
- rb_r = HT2LT(html)->getResourceFactory()
- ->createRadioButtonResource(rb_r, false);
- embed = new dw::core::ui::Embed (rb_r);
+ rb_r = (RadioButtonResource*) input->embed->getResource();
+ resource = factory->createRadioButtonResource(rb_r, false);
init_val = (a_Html_get_attr(html, tag, tagsize, "checked") != NULL);
- init_str = (value) ? value : NULL;
+ init_str = value;
} else if (!dStrcasecmp(type, "hidden")) {
inp_type = DILLO_HTML_INPUT_HIDDEN;
- if (value)
- init_str = dStrdup(a_Html_get_attr(html, tag, tagsize, "value"));
+ init_str = value;
+ int size = Html_input_get_size(html, NULL);
+ resource = factory->createEntryResource(size, false, name);
} else if (!dStrcasecmp(type, "submit")) {
inp_type = DILLO_HTML_INPUT_SUBMIT;
init_str = (value) ? value : dStrdup("submit");
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else if (!dStrcasecmp(type, "reset")) {
inp_type = DILLO_HTML_INPUT_RESET;
init_str = (value) ? value : dStrdup("Reset");
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else if (!dStrcasecmp(type, "image")) {
if (URL_FLAGS(html->base_url) & URL_SpamSafe) {
@@ -466,10 +481,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
attrbuf = a_Html_get_attr(html, tag, tagsize, "alt");
label = attrbuf ? attrbuf : value ? value : name ? name : "Submit";
init_str = dStrdup(label);
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
} else {
inp_type = DILLO_HTML_INPUT_IMAGE;
@@ -485,7 +497,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
valid = false;
BUG_MSG("Forms with file input MUST use HTTP POST method\n");
MSG("File input ignored in form not using HTTP POST method\n");
- } else if (form->enc != DILLO_HTML_ENC_MULTIPART) {
+ } else if (form->content_type != DILLO_HTML_ENC_MULTIPART) {
valid = false;
BUG_MSG("Forms with file input MUST use multipart/form-data"
" encoding\n");
@@ -496,65 +508,60 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
if (valid) {
inp_type = DILLO_HTML_INPUT_FILE;
init_str = dStrdup("File selector");
- dw::core::ui::LabelButtonResource *lbr =
- HT2LT(html)->getResourceFactory()->
- createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (lbr);
+ resource = factory->createLabelButtonResource(init_str);
}
} else if (!dStrcasecmp(type, "button")) {
inp_type = DILLO_HTML_INPUT_BUTTON;
if (value) {
init_str = value;
- dw::core::ui::LabelButtonResource *label_b_r =
- HT2LT(html)->getResourceFactory()
- ->createLabelButtonResource(init_str);
- embed = new dw::core::ui::Embed (label_b_r);
+ resource = factory->createLabelButtonResource(init_str);
}
} else if (!dStrcasecmp(type, "text") || !*type) {
/* Text input, which also is the default */
inp_type = DILLO_HTML_INPUT_TEXT;
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, false);
- embed = new dw::core::ui::Embed (entryResource);
- init_str = (value) ? value : NULL;
+ attrbuf = a_Html_get_attr(html, tag, tagsize, "size");
+ int size = Html_input_get_size(html, attrbuf);
+ resource = factory->createEntryResource(size, false, NULL);
+ init_str = value;
} else {
/* Unknown input type */
BUG_MSG("Unknown input type: \"%s\"\n", type);
}
+ if (resource)
+ embed = new Embed (resource);
+
if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
Html_add_input(html, inp_type, embed, name,
(init_str) ? init_str : "", init_val);
}
-
- if (embed != NULL && inp_type != DILLO_HTML_INPUT_IMAGE &&
+
+ if (embed != NULL && inp_type != DILLO_HTML_INPUT_IMAGE &&
inp_type != DILLO_HTML_INPUT_UNKNOWN) {
+ if (inp_type == DILLO_HTML_INPUT_HIDDEN) {
+ /* TODO Perhaps do this with access to current form setting */
+ embed->setDisplayed(false);
+ }
if (inp_type == DILLO_HTML_INPUT_TEXT ||
inp_type == DILLO_HTML_INPUT_PASSWORD) {
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
- /* Readonly or not? */
if (a_Html_get_attr(html, tag, tagsize, "readonly"))
- entryres->setEditable(false);
+ ((EntryResource *) resource)->setEditable(false);
-// /* Set width of the entry */
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
-// gtk_widget_set_usize(widget, (strtol(attrbuf, NULL, 10) + 1) *
-// gdk_char_width(widget->style->font, '0'), 0);
-//
// /* Maximum length of the text in the entry */
// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "maxlength")))
// gtk_entry_set_max_length(GTK_ENTRY(widget),
// strtol(attrbuf, NULL, 10));
}
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
-
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ CssPropertyList props;
+ char *tooltip_str = dStrdup(attrbuf);
+
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+ }
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle());
}
dFree(type);
dFree(name);
@@ -570,7 +577,7 @@ void Html_tag_open_input(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
{
DilloUrl *action;
- dw::core::ui::Embed *embed;
+ Embed *embed;
const char *attrbuf;
if (html->InFlags & IN_FORM) {
@@ -582,28 +589,22 @@ void Html_tag_open_isindex(DilloHtml *html, const char *tag, int tagsize)
action = a_Html_url_new(html, attrbuf, NULL, 0);
else
action = a_Url_dup(html->base_url);
-
- html->formNew(DILLO_HTML_METHOD_GET, action, DILLO_HTML_ENC_URLENCODING,
+
+ html->formNew(DILLO_HTML_METHOD_GET, action, DILLO_HTML_ENC_URLENCODED,
html->charset);
html->InFlags |= IN_FORM;
-
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
-
+
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "prompt")))
- DW2TB(html->dw)->addText(attrbuf, S_TOP(html)->style);
-
- dw::core::ui::EntryResource *entryResource =
- HT2LT(html)->getResourceFactory()->createEntryResource (10, false);
- embed = new dw::core::ui::Embed (entryResource);
+ HT2TB(html)->addText(attrbuf, html->styleEngine->wordStyle ());
+
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ EntryResource *entryResource = factory->createEntryResource (20,false,NULL);
+ embed = new Embed (entryResource);
Html_add_input(html, DILLO_HTML_INPUT_INDEX, embed, NULL, NULL, FALSE);
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
a_Url_free(action);
html->InFlags &= ~IN_FORM;
@@ -620,7 +621,7 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
char *name;
const char *attrbuf;
int cols, rows;
-
+
if (html->InFlags & IN_TEXTAREA) {
BUG_MSG("nested <textarea>\n");
html->ReqTagClose = TRUE;
@@ -630,22 +631,28 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("<textarea> element inside <select>\n");
return;
}
-
+
html->InFlags |= IN_TEXTAREA;
a_Html_stash_init(html);
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
-
- cols = 20;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cols")))
+
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cols"))) {
cols = strtol(attrbuf, NULL, 10);
+ } else {
+ BUG_MSG("cols attribute is required for <textarea>\n");
+ cols = 20;
+ }
if (cols < 1 || cols > MAX_COLS) {
int badCols = cols;
cols = (cols < 1 ? 20 : MAX_COLS);
BUG_MSG("textarea cols=%d, using cols=%d instead\n", badCols, cols);
}
- rows = 10;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows")))
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rows"))) {
rows = strtol(attrbuf, NULL, 10);
+ } else {
+ BUG_MSG("rows attribute is required for <textarea>\n");
+ rows = 10;
+ }
if (rows < 1 || rows > MAX_ROWS) {
int badRows = rows;
rows = (rows < 1 ? 2 : MAX_ROWS);
@@ -655,24 +662,17 @@ void Html_tag_open_textarea(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name")))
name = dStrdup(attrbuf);
- dw::core::ui::MultiLineTextResource *textres =
- HT2LT(html)->getResourceFactory()->createMultiLineTextResource (cols,
- rows);
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ MultiLineTextResource *textres =
+ factory->createMultiLineTextResource (cols, rows);
- dw::core::ui::Embed *embed = new dw::core::ui::Embed(textres);
+ Embed *embed = new Embed(textres);
/* Readonly or not? */
if (a_Html_get_attr(html, tag, tagsize, "readonly"))
textres->setEditable(false);
Html_add_input(html, DILLO_HTML_INPUT_TEXTAREA, embed, name, NULL, false);
- Color *bg;
- if (prefs.standard_widget_colors)
- bg = NULL;
- else
- bg = Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- HTML_SET_TOP_ATTR(html, backgroundColor, bg);
-
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
dFree(name);
}
@@ -692,7 +692,7 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
dStr_erase(html->Stash, 0, 1);
if (html->Stash->str[0] == '\n')
dStr_erase(html->Stash, 0, 1);
-
+
/* As the spec recommends to canonicalize line endings, it is safe
* to replace '\r' with '\n'. It will be canonicalized anyway! */
for (i = 0; i < html->Stash->len; ++i) {
@@ -703,17 +703,15 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
html->Stash->str[i] = '\n';
}
}
-
+
/* The HTML3.2 spec says it can have "text and character entities". */
str = a_Html_parse_entities(html, html->Stash->str, html->Stash->len);
input = Html_get_current_input(html);
input->init_str = str;
- ((dw::core::ui::MultiLineTextResource *)input->embed->getResource ())
- ->setText(str);
+ ((MultiLineTextResource *)input->embed->getResource ())->setText(str);
html->InFlags &= ~IN_TEXTAREA;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -722,8 +720,8 @@ void Html_tag_close_textarea(DilloHtml *html, int TagIdx)
/* The select tag is quite tricky, because of gorpy html syntax. */
void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
{
-// const char *attrbuf;
-// int size, type, multi;
+ const char *attrbuf;
+ int rows = 0;
if (html->InFlags & IN_SELECT) {
BUG_MSG("nested <select>\n");
@@ -733,51 +731,32 @@ void Html_tag_open_select(DilloHtml *html, const char *tag, int tagsize)
html->InFlags &= ~IN_OPTION;
char *name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
- dw::core::ui::ResourceFactory *factory =
- HT2LT(html)->getResourceFactory ();
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory ();
DilloHtmlInputType type;
- dw::core::ui::SelectionResource *res;
- if (a_Html_get_attr(html, tag, tagsize, "multiple")) {
- type = DILLO_HTML_INPUT_SEL_LIST;
- res = factory->createListResource (
- dw::core::ui::ListResource::SELECTION_MULTIPLE);
- } else {
- type = DILLO_HTML_INPUT_SELECT;
- res = factory->createOptionMenuResource ();
+ SelectionResource *res;
+ bool multi = a_Html_get_attr(html, tag, tagsize, "multiple") != NULL;
+
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size"))) {
+ rows = strtol(attrbuf, NULL, 10);
+ if (rows > 100)
+ rows = 100;
}
- dw::core::ui::Embed *embed = new dw::core::ui::Embed(res);
+ if (rows < 1)
+ rows = multi ? 10 : 1;
- int bg;
- if (prefs.standard_widget_colors) {
- /* Valid colors required; SELECT can contain other elements (BUG) */
- HTML_SET_TOP_ATTR(html, color, Color::createSimple (HT2LT(html), 0));
- bg = 0xffffff;
+ if (rows == 1 && multi == false) {
+ type = DILLO_HTML_INPUT_SELECT;
+ res = factory->createOptionMenuResource ();
} else {
- bg = S_TOP(html)->current_bg_color;
+ type = DILLO_HTML_INPUT_SEL_LIST;
+ res = factory->createListResource (multi ?
+ ListResource::SELECTION_MULTIPLE :
+ ListResource::SELECTION_EXACTLY_ONE,
+ rows);
}
- HTML_SET_TOP_ATTR(html, backgroundColor,
- Color::createShaded (HT2LT(html), bg));
- DW2TB(html->dw)->addWidget (embed, S_TOP(html)->style);
+ Embed *embed = new Embed(res);
-// size = 0;
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
-// size = strtol(attrbuf, NULL, 10);
-//
-// multi = (a_Html_get_attr(html, tag, tagsize, "multiple")) ? 1 : 0;
-// if (size < 1)
-// size = multi ? 10 : 1;
-//
-// if (size == 1) {
-// menu = gtk_menu_new();
-// widget = gtk_option_menu_new();
-// type = DILLO_HTML_INPUT_SELECT;
-// } else {
-// menu = gtk_list_new();
-// widget = menu;
-// if (multi)
-// gtk_list_set_selection_mode(GTK_LIST(menu), GTK_SELECTION_MULTIPLE);
-// type = DILLO_HTML_INPUT_SEL_LIST;
-// }
+ HT2TB(html)->addWidget (embed, html->styleEngine->backgroundStyle ());
Html_add_input(html, type, embed, name, NULL, false);
a_Html_stash_init(html);
@@ -798,15 +777,12 @@ void Html_tag_close_select(DilloHtml *html, int TagIdx)
DilloHtmlInput *input = Html_get_current_input(html);
DilloHtmlSelect *select = input->select;
- // BUG(?): should not do this for MULTI selections
+ // BUG(?): should not do this for MULTI selections
select->ensureSelection ();
- dw::core::ui::SelectionResource *res =
- (dw::core::ui::SelectionResource*)input->embed->getResource();
+ SelectionResource *res = (SelectionResource*)input->embed->getResource();
select->addOptionsTo (res);
}
-
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -826,13 +802,10 @@ void Html_tag_open_option(DilloHtml *html, const char *tag, int tagsize)
if (input->type == DILLO_HTML_INPUT_SELECT ||
input->type == DILLO_HTML_INPUT_SEL_LIST) {
- char *value =
- a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
- bool selected =
- (a_Html_get_attr(html, tag, tagsize, "selected") != NULL);
- bool enabled =
- (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
- input->select->addOption(value,selected,enabled);
+ char *value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
+ bool selected = (a_Html_get_attr(html, tag, tagsize,"selected") != NULL);
+ bool enabled = (a_Html_get_attr(html, tag, tagsize, "disabled") == NULL);
+ input->select->addOption(value, selected, enabled);
}
a_Html_stash_init(html);
@@ -876,37 +849,23 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
if (inp_type != DILLO_HTML_INPUT_UNKNOWN) {
/* Render the button */
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style;
- dw::core::Widget *page;
- dw::core::ui::Embed *embed;
+ Widget *page;
+ Embed *embed;
char *name, *value;
- style_attrs = *S_TOP(html)->style;
- style_attrs.margin.setVal(0);
- style_attrs.borderWidth.setVal(0);
- style_attrs.padding.setVal(0);
- style_attrs.backgroundColor =
- Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
- style = Style::create (HT2LT(html), &style_attrs);
-
page = new Textblock (prefs.limit_text_width);
- page->setStyle (style);
+ page->setStyle (html->styleEngine->backgroundStyle ());
- dw::core::ui::ComplexButtonResource *complex_b_r =
- HT2LT(html)->getResourceFactory()
- ->createComplexButtonResource(page, true);
- embed = new dw::core::ui::Embed(complex_b_r);
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ Resource *resource = factory->createComplexButtonResource(page, true);
+ embed = new Embed(resource);
// a_Dw_button_set_sensitive (DW_BUTTON (button), FALSE);
- DW2TB(html->dw)->addParbreak (5, style);
- DW2TB(html->dw)->addWidget (embed, style);
- DW2TB(html->dw)->addParbreak (5, style);
- style->unref ();
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+ HT2TB(html)->addWidget (embed, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
S_TOP(html)->textblock = html->dw = page;
- /* right button press for menus for button contents */
- html->connectSignals(page);
value = a_Html_get_attr_wdef(html, tag, tagsize, "value", NULL);
name = a_Html_get_attr_wdef(html, tag, tagsize, "name", NULL);
@@ -924,39 +883,39 @@ void Html_tag_open_button(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_close_button(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_BUTTON;
- a_Html_pop_tag(html, TagIdx);
}
/*
- * Class implementations
+ * Class implementations
*/
/*
- * DilloHtmlForm
+ * DilloHtmlForm
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlForm::DilloHtmlForm (DilloHtml *html2,
DilloHtmlMethod method2,
const DilloUrl *action2,
- DilloHtmlEnc enc2,
- const char *charset)
+ DilloHtmlEnc content_type2,
+ const char *charset, bool enabled)
{
html = html2;
method = method2;
action = a_Url_dup(action2);
- enc = enc2;
+ content_type = content_type2;
submit_charset = dStrdup(charset);
inputs = new misc::SimpleVector <DilloHtmlInput*> (4);
num_entry_fields = 0;
- num_submit_buttons = 0;
+ showing_hiddens = false;
+ this->enabled = enabled;
form_receiver = new DilloHtmlReceiver (this);
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlForm::~DilloHtmlForm ()
{
@@ -969,17 +928,18 @@ DilloHtmlForm::~DilloHtmlForm ()
delete(form_receiver);
}
-void DilloHtmlForm::eventHandler(dw::core::ui::Resource *resource)
+void DilloHtmlForm::eventHandler(Resource *resource, EventButton *event)
{
MSG("DilloHtmlForm::eventHandler\n");
- DilloHtmlInput *input = getInput(resource);
- if (input) {
- bool force_submit =
- prefs.enterpress_forces_submit ||
- num_entry_fields == 1;
- input->activate (this, force_submit);
+ if (event && (event->button == 3)) {
+ a_UIcmd_form_popup(html->bw, html->page_url, this, showing_hiddens);
} else {
- MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ DilloHtmlInput *input = getInput(resource);
+ if (input) {
+ input->activate (this, num_entry_fields, event);
+ } else {
+ MSG("DilloHtmlForm::eventHandler: ERROR, input not found!\n");
+ }
}
}
@@ -987,11 +947,21 @@ void DilloHtmlForm::eventHandler(dw::core::ui::Resource *resource)
* Submit.
* (Called by eventHandler())
*/
-void DilloHtmlForm::submit(DilloHtmlInput *input)
+void DilloHtmlForm::submit(DilloHtmlInput *active_input, EventButton *event)
{
- DilloUrl *url = buildQueryUrl(input);
+ DilloUrl *url = buildQueryUrl(active_input);
if (url) {
- a_Nav_push(html->bw, url);
+ if (event && event->button == 2) {
+ if (prefs.middle_click_opens_new_tab) {
+ int focus = prefs.focus_new_tab ? 1 : 0;
+ if (event->state == SHIFT_MASK) focus = !focus;
+ a_UIcmd_open_url_nt(html->bw, url, focus);
+ } else {
+ a_UIcmd_open_url_nw(html->bw, url);
+ }
+ } else {
+ a_UIcmd_open_url(html->bw, url);
+ }
a_Url_free(url);
}
// /* now, make the rendered area have its focus back */
@@ -1002,7 +972,7 @@ void DilloHtmlForm::submit(DilloHtmlInput *input)
* Build a new query URL.
* (Called by submit())
*/
-DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
+DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *active_input)
{
DilloUrl *new_url = NULL;
@@ -1013,11 +983,11 @@ DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
_MSG("DilloHtmlForm::buildQueryUrl: action=%s\n",URL_STR_(action));
- if (num_submit_buttons > 0) {
- if ((input->type == DILLO_HTML_INPUT_SUBMIT) ||
- (input->type == DILLO_HTML_INPUT_IMAGE) ||
- (input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT)) {
- active_submit = input;
+ if (active_input) {
+ if ((active_input->type == DILLO_HTML_INPUT_SUBMIT) ||
+ (active_input->type == DILLO_HTML_INPUT_IMAGE) ||
+ (active_input->type == DILLO_HTML_INPUT_BUTTON_SUBMIT)) {
+ active_submit = active_input;
}
}
@@ -1031,7 +1001,7 @@ DilloUrl *DilloHtmlForm::buildQueryUrl(DilloHtmlInput *input)
/* new_url keeps the dStr and sets DataStr to NULL */
a_Url_set_data(new_url, &DataStr);
a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_Post);
- if (enc == DILLO_HTML_ENC_MULTIPART)
+ if (content_type == DILLO_HTML_ENC_MULTIPART)
a_Url_set_flags(new_url, URL_FLAGS(new_url) | URL_MultipartEnc);
} else {
/* remove <fragment> and <query> sections if present */
@@ -1063,11 +1033,11 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
{
Dstr *DataStr = NULL;
char *boundary = NULL;
- iconv_t encoder = (iconv_t) -1;
+ iconv_t char_encoder = (iconv_t) -1;
if (submit_charset && dStrcasecmp(submit_charset, "UTF-8")) {
- encoder = iconv_open(submit_charset, "UTF-8");
- if (encoder == (iconv_t) -1) {
+ char_encoder = iconv_open(submit_charset, "UTF-8");
+ if (char_encoder == (iconv_t) -1) {
MSG_WARN("Cannot convert to character encoding '%s'\n",
submit_charset);
} else {
@@ -1075,95 +1045,95 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
}
}
- if (enc == DILLO_HTML_ENC_MULTIPART) {
- if (!(boundary = makeMultipartBoundary(encoder, active_submit)))
+ if (content_type == DILLO_HTML_ENC_MULTIPART) {
+ if (!(boundary = makeMultipartBoundary(char_encoder, active_submit)))
MSG_ERR("Cannot generate multipart/form-data boundary.\n");
}
- if ((enc == DILLO_HTML_ENC_URLENCODING) || (boundary != NULL)) {
+ if ((content_type == DILLO_HTML_ENC_URLENCODED) || (boundary != NULL)) {
Dlist *values = dList_new(5);
DataStr = dStr_sized_new(4096);
- for (int input_idx = 0; input_idx < inputs->size(); input_idx++) {
- DilloHtmlInput *input = inputs->get (input_idx);
+ for (int i = 0; i < inputs->size(); i++) {
+ DilloHtmlInput *input = inputs->get (i);
Dstr *name = dStr_new(input->name);
bool is_active_submit = (input == active_submit);
+ int valcount;
- name = encodeText(encoder, &name);
+ name = encodeText(char_encoder, &name);
input->appendValuesTo(values, is_active_submit);
- if (input->type == DILLO_HTML_INPUT_FILE && dList_length(values) >0) {
- if (dList_length(values) > 1)
- MSG_WARN("multiple files per form control not supported\n");
- Dstr *file = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, file);
-
- /* Get filename and encode it. Do not encode file contents. */
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)
- input->embed->getResource();
- const char *filename = lbr->getLabel();
- if (filename[0] && strcmp(filename, input->init_str)) {
- char *p = strrchr(filename, '/');
- if (p)
- filename = p + 1; /* don't reveal path */
- Dstr *dfilename = dStr_new(filename);
- dfilename = encodeText(encoder, &dfilename);
- appendInputMultipartFiles(DataStr, boundary, name->str,
- file, dfilename->str);
- dStr_free(dfilename, 1);
- }
- dStr_free(file, 1);
- } else if (input->type == DILLO_HTML_INPUT_INDEX) {
- Dstr *val = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, val);
- val = encodeText(encoder, &val);
- urlencodeAppend(DataStr, val->str);
- dStr_free(val, 1);
- } else if (input->type == DILLO_HTML_INPUT_IMAGE) {
- if (dList_length(values) > 0) {
+ if ((valcount = dList_length(values)) > 0) {
+ if (input->type == DILLO_HTML_INPUT_FILE) {
+ if (valcount > 1)
+ MSG_WARN("multiple files per form control not supported\n");
+ Dstr *file = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, file);
+
+ /* Get filename and encode it. Do not encode file contents. */
+ LabelButtonResource *lbr =
+ (LabelButtonResource*) input->embed->getResource();
+ const char *filename = lbr->getLabel();
+ if (filename[0] && strcmp(filename, input->init_str)) {
+ const char *p = strrchr(filename, '/');
+ if (p)
+ filename = p + 1; /* don't reveal path */
+ Dstr *dfilename = dStr_new(filename);
+ dfilename = encodeText(char_encoder, &dfilename);
+ filesInputMultipartAppend(DataStr, boundary, name->str,
+ file, dfilename->str);
+ dStr_free(dfilename, 1);
+ }
+ dStr_free(file, 1);
+ } else if (input->type == DILLO_HTML_INPUT_INDEX) {
+ /* no name */
+ Dstr *val = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, val);
+ val = encodeText(char_encoder, &val);
+ strUrlencodeAppend(DataStr, val->str);
+ dStr_free(val, 1);
+ } else if (input->type == DILLO_HTML_INPUT_IMAGE) {
Dstr *x, *y;
x = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, x);
y = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, y);
- if (enc == DILLO_HTML_ENC_URLENCODING)
- appendClickposUrlencode(DataStr, name, x, y);
- else if (enc == DILLO_HTML_ENC_MULTIPART)
- appendClickposMultipart(DataStr, boundary, name, x, y);
+ if (content_type == DILLO_HTML_ENC_URLENCODED)
+ imageInputUrlencodeAppend(DataStr, name, x, y);
+ else if (content_type == DILLO_HTML_ENC_MULTIPART)
+ imageInputMultipartAppend(DataStr, boundary, name, x, y);
dStr_free(x, 1);
dStr_free(y, 1);
- }
- } else {
- int length = dList_length(values), i;
- for (i = 0; i < length; i++) {
- Dstr *val = (Dstr *) dList_nth_data(values, 0);
- dList_remove(values, val);
- val = encodeText(encoder, &val);
- if (enc == DILLO_HTML_ENC_URLENCODING)
- appendInputUrlencode(DataStr, name->str, val->str);
- else if (enc == DILLO_HTML_ENC_MULTIPART)
- appendInputMultipart(DataStr, boundary,
- name->str, val->str);
- dStr_free(val, 1);
+ } else {
+ for (int j = 0; j < valcount; j++) {
+ Dstr *val = (Dstr *) dList_nth_data(values, 0);
+ dList_remove(values, val);
+ val = encodeText(char_encoder, &val);
+ if (content_type == DILLO_HTML_ENC_URLENCODED)
+ inputUrlencodeAppend(DataStr, name->str, val->str);
+ else if (content_type == DILLO_HTML_ENC_MULTIPART)
+ inputMultipartAppend(DataStr, boundary, name->str,
+ val->str);
+ dStr_free(val, 1);
+ }
}
}
dStr_free(name, 1);
}
if (DataStr->len > 0) {
- if (enc == DILLO_HTML_ENC_URLENCODING) {
+ if (content_type == DILLO_HTML_ENC_URLENCODED) {
if (DataStr->str[DataStr->len - 1] == '&')
dStr_truncate(DataStr, DataStr->len - 1);
- } else if (enc == DILLO_HTML_ENC_MULTIPART) {
+ } else if (content_type == DILLO_HTML_ENC_MULTIPART) {
dStr_append(DataStr, "--");
}
}
dList_free(values);
}
dFree(boundary);
- if (encoder != (iconv_t) -1)
- (void)iconv_close(encoder);
+ if (char_encoder != (iconv_t) -1)
+ (void)iconv_close(char_encoder);
return DataStr;
}
@@ -1171,7 +1141,7 @@ Dstr *DilloHtmlForm::buildQueryData(DilloHtmlInput *active_submit)
* Generate a boundary string for use in separating the parts of a
* multipart/form-data submission.
*/
-char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
+char *DilloHtmlForm::makeMultipartBoundary(iconv_t char_encoder,
DilloHtmlInput *active_submit)
{
const int max_tries = 10;
@@ -1181,25 +1151,25 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
char *ret = NULL;
/* fill DataStr with names, filenames, and values */
- for (int input_idx = 0; input_idx < inputs->size(); input_idx++) {
+ for (int i = 0; i < inputs->size(); i++) {
Dstr *dstr;
- DilloHtmlInput *input = inputs->get (input_idx);
+ DilloHtmlInput *input = inputs->get (i);
bool is_active_submit = (input == active_submit);
input->appendValuesTo(values, is_active_submit);
if (input->name) {
dstr = dStr_new(input->name);
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
if (input->type == DILLO_HTML_INPUT_FILE) {
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)input->embed->getResource();
+ LabelButtonResource *lbr =
+ (LabelButtonResource*)input->embed->getResource();
const char *filename = lbr->getLabel();
if (filename[0] && strcmp(filename, input->init_str)) {
dstr = dStr_new(filename);
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
@@ -1209,7 +1179,7 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
dstr = (Dstr *) dList_nth_data(values, 0);
dList_remove(values, dstr);
if (input->type != DILLO_HTML_INPUT_FILE)
- dstr = encodeText(encoder, &dstr);
+ dstr = encodeText(char_encoder, &dstr);
dStr_append_l(DataStr, dstr->str, dstr->len);
dStr_free(dstr, 1);
}
@@ -1233,9 +1203,9 @@ char *DilloHtmlForm::makeMultipartBoundary(iconv_t encoder,
/*
* Pass input text through character set encoder.
* Return value: same input Dstr if no encoding is needed.
- new Dstr when encoding (input Dstr is freed).
+ * new Dstr when encoding (input Dstr is freed).
*/
-Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
+Dstr *DilloHtmlForm::encodeText(iconv_t char_encoder, Dstr **input)
{
int rc = 0;
Dstr *output;
@@ -1245,7 +1215,7 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
size_t inLeft, outRoom;
bool bad_chars = false;
- if ((encoder == (iconv_t) -1) || *input == NULL || (*input)->len == 0)
+ if ((char_encoder == (iconv_t) -1) || *input == NULL || (*input)->len == 0)
return *input;
output = dStr_new("");
@@ -1258,7 +1228,7 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
outPtr = buffer;
outRoom = bufsize;
- rc = iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
+ rc = iconv(char_encoder, &inPtr, &inLeft, &outPtr, &outRoom);
// iconv() on success, number of bytes converted
// -1, errno == EILSEQ illegal byte sequence found
@@ -1298,28 +1268,27 @@ Dstr *DilloHtmlForm::encodeText(iconv_t encoder, Dstr **input)
return output;
}
-
+
/*
- * Urlencode 'val' and append it to 'str'
+ * Urlencode 'str' and append it to 'dstr'
*/
-void DilloHtmlForm::urlencodeAppend(Dstr *str, const char *val)
+void DilloHtmlForm::strUrlencodeAppend(Dstr *dstr, const char *str)
{
- char *enc_val = a_Url_encode_hex_str(val);
- dStr_append(str, enc_val);
- dFree(enc_val);
+ char *encoded = a_Url_encode_hex_str(str);
+ dStr_append(dstr, encoded);
+ dFree(encoded);
}
/*
* Append a name-value pair to url data using url encoding.
*/
-void DilloHtmlForm::appendInputUrlencode(Dstr *data,
- const char *name,
+void DilloHtmlForm::inputUrlencodeAppend(Dstr *data, const char *name,
const char *value)
{
if (name && name[0]) {
- urlencodeAppend(data, name);
+ strUrlencodeAppend(data, name);
dStr_append_c(data, '=');
- urlencodeAppend(data, value);
+ strUrlencodeAppend(data, value);
dStr_append_c(data, '&');
}
}
@@ -1328,7 +1297,7 @@ void DilloHtmlForm::appendInputUrlencode(Dstr *data,
* Append files to URL data using multipart encoding.
* Currently only accepts one file.
*/
-void DilloHtmlForm::appendInputMultipartFiles(Dstr* data,
+void DilloHtmlForm::filesInputMultipartAppend(Dstr* data,
const char *boundary,
const char *name,
Dstr *file,
@@ -1379,7 +1348,7 @@ void DilloHtmlForm::appendInputMultipartFiles(Dstr* data,
/*
* Append a name-value pair to url data using multipart encoding.
*/
-void DilloHtmlForm::appendInputMultipart(Dstr *data,
+void DilloHtmlForm::inputMultipartAppend(Dstr *data,
const char *boundary,
const char *name,
const char *value)
@@ -1403,13 +1372,13 @@ void DilloHtmlForm::appendInputMultipart(Dstr *data,
/*
* Append an image button click position to url data using url encoding.
*/
-void DilloHtmlForm::appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x,
- Dstr *y)
+void DilloHtmlForm::imageInputUrlencodeAppend(Dstr *data, Dstr *name, Dstr *x,
+ Dstr *y)
{
if (name->len) {
- urlencodeAppend(data, name->str);
+ strUrlencodeAppend(data, name->str);
dStr_sprintfa(data, ".x=%s&", x->str);
- urlencodeAppend(data, name->str);
+ strUrlencodeAppend(data, name->str);
dStr_sprintfa(data, ".y=%s&", y->str);
} else
dStr_sprintfa(data, "x=%s&y=%s&", x->str, y->str);
@@ -1418,8 +1387,8 @@ void DilloHtmlForm::appendClickposUrlencode(Dstr *data, Dstr *name, Dstr *x,
/*
* Append an image button click position to url data using multipart encoding.
*/
-void DilloHtmlForm::appendClickposMultipart(Dstr *data, const char *boundary,
- Dstr *name, Dstr *x, Dstr *y)
+void DilloHtmlForm::imageInputMultipartAppend(Dstr *data, const char *boundary,
+ Dstr *name, Dstr *x, Dstr *y)
{
int orig_len = name->len;
@@ -1427,10 +1396,10 @@ void DilloHtmlForm::appendClickposMultipart(Dstr *data, const char *boundary,
dStr_append_c(name, '.');
dStr_append_c(name, 'x');
- appendInputMultipart(data, boundary, name->str, x->str);
+ inputMultipartAppend(data, boundary, name->str, x->str);
dStr_truncate(name, name->len - 1);
dStr_append_c(name, 'y');
- appendInputMultipart(data, boundary, name->str, y->str);
+ inputMultipartAppend(data, boundary, name->str, y->str);
dStr_truncate(name, orig_len);
}
@@ -1446,11 +1415,33 @@ void DilloHtmlForm::reset ()
}
/*
+ * Show/hide "hidden" form controls
+ */
+void DilloHtmlForm::display_hiddens(bool display)
+{
+ int size = inputs->size();
+ for (int i = 0; i < size; i++) {
+ DilloHtmlInput *input = inputs->get(i);
+ if (input->type == DILLO_HTML_INPUT_HIDDEN) {
+ input->embed->setDisplayed(display);
+ }
+ }
+ showing_hiddens = display;
+}
+
+void DilloHtmlForm::setEnabled(bool enabled)
+{
+ for (int i = 0; i < inputs->size(); i++)
+ inputs->get(i)->setEnabled(enabled);
+}
+
+/*
* Add a new input.
*/
void DilloHtmlForm::addInput(DilloHtmlInput *input, DilloHtmlInputType type)
{
input->connectTo (form_receiver);
+ input->setEnabled (enabled);
int ni = inputs->size ();
inputs->increase ();
inputs->set (ni,input);
@@ -1459,17 +1450,13 @@ void DilloHtmlForm::addInput(DilloHtmlInput *input, DilloHtmlInputType type)
if (type == DILLO_HTML_INPUT_PASSWORD ||
type == DILLO_HTML_INPUT_TEXT) {
num_entry_fields++;
- } else if (type == DILLO_HTML_INPUT_SUBMIT ||
- type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
- type == DILLO_HTML_INPUT_IMAGE) {
- num_submit_buttons++;
}
}
/*
* Return the input with a given resource.
*/
-DilloHtmlInput *DilloHtmlForm::getInput (dw::core::ui::Resource *resource)
+DilloHtmlInput *DilloHtmlForm::getInput (Resource *resource)
{
for (int idx = 0; idx < inputs->size(); idx++) {
DilloHtmlInput *input = inputs->get(idx);
@@ -1495,22 +1482,22 @@ DilloHtmlInput *DilloHtmlForm::getRadioInput (const char *name)
}
/*
- * DilloHtmlReceiver
+ * DilloHtmlReceiver
*
* TODO: Currently there's "clicked" for buttons, we surely need "enter" for
* textentries, and maybe the "mouseover, ...." set for Javascript.
*/
-void DilloHtmlReceiver::activate (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::activate (Resource *resource)
{
- form->eventHandler(resource);
+ form->eventHandler(resource, NULL);
}
/*
* Enter a form control, as in "onmouseover".
* For _pressing_ enter in a text control, see activate().
*/
-void DilloHtmlReceiver::enter (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::enter (Resource *resource)
{
DilloHtml *html = form->html;
DilloHtmlInput *input = form->getInput(resource);
@@ -1532,29 +1519,27 @@ void DilloHtmlReceiver::enter (dw::core::ui::Resource *resource)
/*
* Leave a form control, or "onmouseout".
*/
-void DilloHtmlReceiver::leave (dw::core::ui::Resource *resource)
+void DilloHtmlReceiver::leave (Resource *resource)
{
DilloHtml *html = form->html;
a_UIcmd_set_msg(html->bw, "");
}
-void DilloHtmlReceiver::clicked (dw::core::ui::ButtonResource *resource,
- int buttonNo, int x, int y)
+void DilloHtmlReceiver::clicked (Resource *resource,
+ EventButton *event)
{
-// form->eventHandler(resource, x, y);
+ form->eventHandler(resource, event);
}
/*
- * DilloHtmlInput
+ * DilloHtmlInput
*/
/*
- * Constructor
+ * Constructor
*/
-DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2,
- dw::core::ui::Embed *embed2,
- const char *name2,
- const char *init_str2,
+DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2, Embed *embed2,
+ const char *name2, const char *init_str2,
bool init_val2)
{
type = type2;
@@ -1576,7 +1561,7 @@ DilloHtmlInput::DilloHtmlInput (DilloHtmlInputType type2,
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlInput::~DilloHtmlInput ()
{
@@ -1592,61 +1577,57 @@ DilloHtmlInput::~DilloHtmlInput ()
*/
void DilloHtmlInput::connectTo(DilloHtmlReceiver *form_receiver)
{
- dw::core::ui::Resource *resource = NULL;
- if (embed)
- resource = embed->getResource ();
- switch (type) {
- case DILLO_HTML_INPUT_UNKNOWN:
- case DILLO_HTML_INPUT_HIDDEN:
- case DILLO_HTML_INPUT_CHECKBOX:
- case DILLO_HTML_INPUT_RADIO:
- case DILLO_HTML_INPUT_BUTTON:
- case DILLO_HTML_INPUT_TEXTAREA:
- case DILLO_HTML_INPUT_SELECT:
- case DILLO_HTML_INPUT_SEL_LIST:
- // do nothing
- break;
- case DILLO_HTML_INPUT_TEXT:
- case DILLO_HTML_INPUT_PASSWORD:
- case DILLO_HTML_INPUT_INDEX:
- case DILLO_HTML_INPUT_SUBMIT:
- case DILLO_HTML_INPUT_RESET:
- case DILLO_HTML_INPUT_BUTTON_SUBMIT:
- case DILLO_HTML_INPUT_BUTTON_RESET:
- case DILLO_HTML_INPUT_IMAGE:
- case DILLO_HTML_INPUT_FILE:
- if (resource)
+ Resource *resource;
+ if (embed && (resource = embed->getResource())) {
+ resource->connectClicked (form_receiver);
+ if (type == DILLO_HTML_INPUT_SUBMIT ||
+ type == DILLO_HTML_INPUT_RESET ||
+ type == DILLO_HTML_INPUT_BUTTON_SUBMIT ||
+ type == DILLO_HTML_INPUT_BUTTON_RESET ||
+ type == DILLO_HTML_INPUT_IMAGE ||
+ type == DILLO_HTML_INPUT_FILE ||
+ type == DILLO_HTML_INPUT_TEXT ||
+ type == DILLO_HTML_INPUT_PASSWORD ||
+ type == DILLO_HTML_INPUT_INDEX) {
resource->connectActivate (form_receiver);
- break;
+ }
}
}
/*
- * Activate a form
+ * Activate a form
*/
-void DilloHtmlInput::activate(DilloHtmlForm *form, bool force_submit)
+void DilloHtmlInput::activate(DilloHtmlForm *form, int num_entry_fields,
+ EventButton *event)
{
switch (type) {
- case DILLO_HTML_INPUT_TEXT:
- case DILLO_HTML_INPUT_PASSWORD:
- if (force_submit)
- form->submit (this);
- break;
case DILLO_HTML_INPUT_FILE:
readFile (form->html->bw);
break;
case DILLO_HTML_INPUT_RESET:
case DILLO_HTML_INPUT_BUTTON_RESET:
- form->reset ();
+ form->reset();
+ break;
+ case DILLO_HTML_INPUT_TEXT:
+ case DILLO_HTML_INPUT_PASSWORD:
+ if (!(prefs.enterpress_forces_submit || num_entry_fields == 1)) {
+ break;
+ } else {
+ /* fall through */
+ }
+ case DILLO_HTML_INPUT_SUBMIT:
+ case DILLO_HTML_INPUT_BUTTON_SUBMIT:
+ case DILLO_HTML_INPUT_IMAGE:
+ case DILLO_HTML_INPUT_INDEX:
+ form->submit(this, event);
break;
default:
- form->submit (this);
break;
}
}
/*
- * Read a file into cache
+ * Read a file into cache
*/
void DilloHtmlInput::readFile (BrowserWindow *bw)
{
@@ -1657,8 +1638,7 @@ void DilloHtmlInput::readFile (BrowserWindow *bw)
file_data = a_Misc_file2dstr(filename);
if (file_data) {
a_UIcmd_set_msg(bw, "File loaded.");
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
lbr->setLabel(filename);
} else {
a_UIcmd_set_msg(bw, "ERROR: can't load: %s", filename);
@@ -1675,24 +1655,24 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
case DILLO_HTML_INPUT_TEXT:
case DILLO_HTML_INPUT_PASSWORD:
case DILLO_HTML_INPUT_INDEX:
+ case DILLO_HTML_INPUT_HIDDEN:
{
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
+ EntryResource *entryres = (EntryResource*)embed->getResource();
dList_append(values, dStr_new(entryres->getText()));
}
break;
case DILLO_HTML_INPUT_TEXTAREA:
{
- dw::core::ui::MultiLineTextResource *textres =
- (dw::core::ui::MultiLineTextResource*)embed->getResource();
+ MultiLineTextResource *textres =
+ (MultiLineTextResource*)embed->getResource();
dList_append(values, dStr_new(textres->getText()));
}
break;
case DILLO_HTML_INPUT_CHECKBOX:
case DILLO_HTML_INPUT_RADIO:
{
- dw::core::ui::ToggleButtonResource *cb_r =
- (dw::core::ui::ToggleButtonResource*)embed->getResource();
+ ToggleButtonResource *cb_r =
+ (ToggleButtonResource*)embed->getResource();
if (name && init_str && cb_r->isActivated()) {
dList_append(values, dStr_new(init_str));
}
@@ -1703,21 +1683,16 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
if (is_active_submit)
dList_append(values, dStr_new(init_str));
break;
- case DILLO_HTML_INPUT_HIDDEN:
- dList_append(values, dStr_new(init_str));
- break;
case DILLO_HTML_INPUT_SELECT:
case DILLO_HTML_INPUT_SEL_LIST:
- { // brackets for compiler happiness.
- dw::core::ui::SelectionResource *sel_res =
- (dw::core::ui::SelectionResource*)embed->getResource();
+ {
+ SelectionResource *sel_res = (SelectionResource*)embed->getResource();
select->appendValuesTo (values, sel_res);
}
break;
case DILLO_HTML_INPUT_FILE:
{
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
const char *filename = lbr->getLabel();
if (filename[0] && strcmp(filename, init_str)) {
if (file_data) {
@@ -1732,8 +1707,8 @@ void DilloHtmlInput::appendValuesTo(Dlist *values, bool is_active_submit)
break;
case DILLO_HTML_INPUT_IMAGE:
if (is_active_submit) {
- dw::core::ui::ComplexButtonResource *cbr =
- (dw::core::ui::ComplexButtonResource*)embed->getResource();
+ ComplexButtonResource *cbr =
+ (ComplexButtonResource*)embed->getResource();
Dstr *strX = dStr_new("");
Dstr *strY = dStr_new("");
dStr_sprintf(strX, "%d", cbr->getClickX());
@@ -1755,17 +1730,18 @@ void DilloHtmlInput::reset ()
switch (type) {
case DILLO_HTML_INPUT_TEXT:
case DILLO_HTML_INPUT_PASSWORD:
+ case DILLO_HTML_INPUT_INDEX:
+ case DILLO_HTML_INPUT_HIDDEN:
{
- dw::core::ui::EntryResource *entryres =
- (dw::core::ui::EntryResource*)embed->getResource();
+ EntryResource *entryres = (EntryResource*)embed->getResource();
entryres->setText(init_str ? init_str : "");
}
break;
case DILLO_HTML_INPUT_CHECKBOX:
case DILLO_HTML_INPUT_RADIO:
{
- dw::core::ui::ToggleButtonResource *tb_r =
- (dw::core::ui::ToggleButtonResource*)embed->getResource();
+ ToggleButtonResource *tb_r =
+ (ToggleButtonResource*)embed->getResource();
tb_r->setActivated(init_val);
}
break;
@@ -1801,15 +1777,14 @@ void DilloHtmlInput::reset ()
break;
case DILLO_HTML_INPUT_TEXTAREA:
if (init_str != NULL) {
- dw::core::ui::MultiLineTextResource *textres =
- (dw::core::ui::MultiLineTextResource*)embed->getResource();
+ MultiLineTextResource *textres =
+ (MultiLineTextResource*)embed->getResource();
textres->setText(init_str ? init_str : "");
}
break;
case DILLO_HTML_INPUT_FILE:
{
- dw::core::ui::LabelButtonResource *lbr =
- (dw::core::ui::LabelButtonResource*)embed->getResource();
+ LabelButtonResource *lbr = (LabelButtonResource*)embed->getResource();
lbr->setLabel(init_str);
}
break;
@@ -1819,11 +1794,11 @@ void DilloHtmlInput::reset ()
}
/*
- * DilloHtmlSelect
+ * DilloHtmlSelect
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlSelect::DilloHtmlSelect ()
{
@@ -1831,7 +1806,7 @@ DilloHtmlSelect::DilloHtmlSelect ()
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlSelect::~DilloHtmlSelect ()
{
@@ -1872,7 +1847,7 @@ void DilloHtmlSelect::ensureSelection()
}
}
-void DilloHtmlSelect::addOptionsTo (dw::core::ui::SelectionResource *res)
+void DilloHtmlSelect::addOptionsTo (SelectionResource *res)
{
int size = options->size ();
for (int i = 0; i < size; i++) {
@@ -1881,8 +1856,7 @@ void DilloHtmlSelect::addOptionsTo (dw::core::ui::SelectionResource *res)
}
}
-void DilloHtmlSelect::appendValuesTo (Dlist *values,
- dw::core::ui::SelectionResource *res)
+void DilloHtmlSelect::appendValuesTo (Dlist *values, SelectionResource *res)
{
int size = options->size ();
for (int i = 0; i < size; i++) {
@@ -1895,11 +1869,11 @@ void DilloHtmlSelect::appendValuesTo (Dlist *values,
}
/*
- * DilloHtmlOption
+ * DilloHtmlOption
*/
/*
- * Constructor
+ * Constructor
*/
DilloHtmlOption::DilloHtmlOption (char *value2,
bool selected2,
@@ -1912,7 +1886,7 @@ DilloHtmlOption::DilloHtmlOption (char *value2,
}
/*
- * Destructor
+ * Destructor
*/
DilloHtmlOption::~DilloHtmlOption ()
{
@@ -1921,48 +1895,38 @@ DilloHtmlOption::~DilloHtmlOption ()
}
/*
- * Utilities
+ * Utilities
*/
/*
* Create input image for the form
*/
-static dw::core::ui::Embed *Html_input_image(DilloHtml *html,
- const char *tag, int tagsize)
+static Embed *Html_input_image(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- StyleAttrs style_attrs;
DilloImage *Image;
- dw::core::ui::Embed *button = NULL;
+ Embed *button = NULL;
DilloUrl *url = NULL;
-
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "src")) &&
(url = a_Html_url_new(html, attrbuf, NULL, 0))) {
- style_attrs = *S_TOP(html)->style;
- style_attrs.cursor = CURSOR_POINTER;
- style_attrs.backgroundColor =
- style::Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color);
+
+ html->styleEngine->setPseudoLink ();
/* create new image and add it to the button */
- if ((Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs,
- false))) {
- Style *style = Style::create (HT2LT(html), &style_attrs);
- IM2DW(Image)->setStyle (style);
- dw::core::ui::ComplexButtonResource *complex_b_r =
- HT2LT(html)->getResourceFactory()->createComplexButtonResource(
- IM2DW(Image), false);
- button = new dw::core::ui::Embed(complex_b_r);
- DW2TB(html->dw)->addWidget (button, style);
+ if ((Image = a_Html_image_new(html, tag, tagsize, url))) {
+ IM2DW(Image)->setStyle (html->styleEngine->backgroundStyle ());
+ ResourceFactory *factory = HT2LT(html)->getResourceFactory();
+ ComplexButtonResource *complex_b_r =
+ factory->createComplexButtonResource(IM2DW(Image), false);
+ button = new Embed(complex_b_r);
+ HT2TB(html)->addWidget (button, html->styleEngine->style ());
// gtk_widget_set_sensitive(widget, FALSE); /* Until end of FORM! */
- style->unref();
- /* a right button press brings up the image menu */
- html->connectSignals((Widget*)Image->dw);
} else {
a_Url_free(url);
}
}
-
if (!button)
MSG("Html_input_image: unable to create image submit.\n");
return button;
@@ -1978,7 +1942,6 @@ static void Html_option_finish(DilloHtml *html)
input->type == DILLO_HTML_INPUT_SEL_LIST) {
DilloHtmlOption *option =
input->select->getCurrentOption ();
- option->content =
- a_Html_parse_entities(html, html->Stash->str, html->Stash->len);
+ option->content = dStrndup(html->Stash->str, html->Stash->len);
}
}
diff --git a/src/form.hh b/src/form.hh
index 8375ba50..a54cde56 100644
--- a/src/form.hh
+++ b/src/form.hh
@@ -4,7 +4,7 @@
#include "url.h"
/*
- * Typedefs
+ * Typedefs
*/
typedef enum {
@@ -14,12 +14,12 @@ typedef enum {
} DilloHtmlMethod;
typedef enum {
- DILLO_HTML_ENC_URLENCODING,
+ DILLO_HTML_ENC_URLENCODED,
DILLO_HTML_ENC_MULTIPART
} DilloHtmlEnc;
/*
- * Classes
+ * Classes
*/
class DilloHtmlForm;
@@ -27,20 +27,24 @@ class DilloHtmlInput;
class DilloHtml;
/*
- * Form API
+ * Form API
*/
DilloHtmlForm *a_Html_form_new(DilloHtml *html,
DilloHtmlMethod method,
const DilloUrl *action,
DilloHtmlEnc enc,
- const char *charset);
+ const char *charset, bool enabled);
void a_Html_form_delete(DilloHtmlForm* form);
void a_Html_input_delete(DilloHtmlInput* input);
+void a_Html_form_submit2(void *v_form);
+void a_Html_form_reset2(void *v_form);
+void a_Html_form_display_hiddens2(void *v_form, bool display);
+
/*
- * Form parsing functions
+ * Form parsing functions
*/
void Html_tag_open_form(DilloHtml *html, const char *tag, int tagsize);
diff --git a/src/gif.c b/src/gif.c
index 6ea23561..3df83d20 100644
--- a/src/gif.c
+++ b/src/gif.c
@@ -67,10 +67,8 @@
#include "msg.h"
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
-#include "prefs.h"
#define INTERLACE 0x40
#define LOCALCOLORMAP 0x80
@@ -147,54 +145,15 @@ static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize);
static void Gif_close(DilloGif *gif, CacheClient_t *Client);
static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *buf,
int bufsize, void *Buf);
-static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version);
-static void Gif_callback(int Op, CacheClient_t *Client);
-
-/* exported function */
-void *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data);
/*
- * MIME handler for "image/gif" type
- * (Sets Gif_callback as cache-client)
- */
-void *a_Gif_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data)
-{
- DilloWeb *web = Ptr;
- DICacheEntry *DicEntry;
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
- /* TODO: get the backgound color from the parent widget -- Livio. */
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Gif_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Gif_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
-}
-
-/*
* Create a new gif structure for decoding a gif into a RGB buffer
*/
-static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Gif_new(DilloImage *Image, DilloUrl *url, int version)
{
DilloGif *gif = dMalloc(sizeof(DilloGif));
+ _MSG("a_Gif_new: gif=%p\n", gif);
gif->Image = Image;
gif->url = url;
@@ -216,15 +175,38 @@ static DilloGif *Gif_new(DilloImage *Image, DilloUrl *url, int version)
}
/*
+ * Free the gif-decoding data structure.
+ */
+static void Gif_free(DilloGif *gif)
+{
+ int i;
+
+ _MSG("Gif_free: gif=%p\n", gif);
+
+ dFree(gif->linebuf);
+ if (gif->spill_lines != NULL) {
+ for (i = 0; i < gif->num_spill_lines_max; i++)
+ dFree(gif->spill_lines[i]);
+ dFree(gif->spill_lines);
+ }
+ dFree(gif);
+}
+
+/*
* This function is a cache client, it receives data from the cache
* and dispatches it to the appropriate gif-processing functions
*/
-static void Gif_callback(int Op, CacheClient_t *Client)
+void a_Gif_callback(int Op, void *data)
{
- if (Op)
- Gif_close(Client->CbData, Client);
- else
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
Gif_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Gif_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Gif_free(data);
+ }
}
/*
@@ -259,20 +241,9 @@ static void Gif_write(DilloGif *gif, void *Buf, uint_t BufSize)
*/
static void Gif_close(DilloGif *gif, CacheClient_t *Client)
{
- int i;
-
- _MSG("destroy gif %p\n", gif);
-
+ _MSG("Gif_close: destroy gif %p\n", gif);
a_Dicache_close(gif->url, gif->version, Client);
-
- dFree(gif->linebuf);
-
- if (gif->spill_lines != NULL) {
- for (i = 0; i < gif->num_spill_lines_max; i++)
- dFree(gif->spill_lines[i]);
- dFree(gif->spill_lines);
- }
- dFree(gif);
+ Gif_free(gif);
}
@@ -418,7 +389,7 @@ static void Gif_lwz_init(DilloGif *gif)
*/
static void Gif_emit_line(DilloGif *gif, const uchar_t *linebuf)
{
- a_Dicache_write(gif->Image, gif->url, gif->version, linebuf, gif->y);
+ a_Dicache_write(gif->url, gif->version, linebuf, gif->y);
if (gif->Flags & INTERLACE) {
switch (gif->pass) {
case 0:
@@ -841,6 +812,16 @@ static size_t Gif_do_img_desc(DilloGif *gif, void *Buf,
gif->Width = LM_to_uint(buf[4], buf[5]);
gif->Height = LM_to_uint(buf[6], buf[7]);
+
+ /* check max image size */
+ if (gif->Width <= 0 || gif->Height <= 0 ||
+ gif->Width > IMAGE_MAX_AREA / gif->Height) {
+ MSG("Gif_do_img_desc: suspicious image size request %ux%u\n",
+ gif->Width, gif->Height);
+ gif->state = 999;
+ return 0;
+ }
+
gif->linebuf = dMalloc(gif->Width);
a_Dicache_set_parms(gif->url, gif->version, gif->Image,
@@ -1045,4 +1026,9 @@ static size_t Gif_process_bytes(DilloGif *gif, const uchar_t *ibuf,
return bufsize - tmp_bufsize;
}
+#else /* ENABLE_GIF */
+
+void *a_Gif_new() { return 0; }
+void a_Gif_callback() { return; }
+
#endif /* ENABLE_GIF */
diff --git a/src/history.c b/src/history.c
index 4b8fb426..15ab19db 100644
--- a/src/history.c
+++ b/src/history.c
@@ -75,22 +75,9 @@ int a_History_add_url(DilloUrl *url)
}
/*
- * Set the page-title for a given URL (by idx)
- * (this is known when the first chunks of HTML data arrive)
- */
-int a_History_set_title(int idx, const char *title)
-{
- dReturn_val_if_fail(idx >= 0 && idx < history_size, 0);
-
- dFree(history[idx].title);
- history[idx].title = dStrdup(title);
- return 1;
-}
-
-/*
* Return the DilloUrl field (by index)
*/
-DilloUrl *a_History_get_url(int idx)
+const DilloUrl *a_History_get_url(int idx)
{
_MSG("a_History_get_url: ");
/* History_show(); */
@@ -137,11 +124,32 @@ const char *a_History_get_title_by_url(const DilloUrl *url, int force)
return NULL;
}
+/*
+ * Set the page-title for a given URL
+ */
+void a_History_set_title_by_url(const DilloUrl *url, const char *title)
+{
+ int i;
+
+ dReturn_if (url == NULL);
+
+ for (i = history_size - 1; i >= 0; --i)
+ if (a_Url_cmp(url, history[i].url) == 0)
+ break;
+
+ if (i >= 0) {
+ dFree(history[i].title);
+ history[i].title = dStrdup(title);
+ } else {
+ MSG_ERR("a_History_set_title_by_url: %s not found\n", URL_STR(url));
+ }
+}
+
/*
* Free all the memory used by this module
*/
-void a_History_free()
+void a_History_freeall()
{
int i;
diff --git a/src/history.h b/src/history.h
index da520c87..beb0cdf9 100644
--- a/src/history.h
+++ b/src/history.h
@@ -10,11 +10,11 @@ extern "C" {
#endif /* __cplusplus */
int a_History_add_url(DilloUrl *url);
-int a_History_set_title(int idx, const char *title);
-DilloUrl *a_History_get_url(int idx);
+void a_History_set_title_by_url(const DilloUrl *url, const char *title);
+const DilloUrl *a_History_get_url(int idx);
const char *a_History_get_title(int idx, int force);
const char *a_History_get_title_by_url(const DilloUrl *url, int force);
-void a_History_free(void);
+void a_History_freeall(void);
#ifdef __cplusplus
diff --git a/src/html.cc b/src/html.cc
index 70466979..49d06da3 100644
--- a/src/html.cc
+++ b/src/html.cc
@@ -20,20 +20,17 @@
#include <string.h> /* for memcpy and memmove */
#include <stdlib.h>
#include <stdio.h> /* for sprintf */
-#include <math.h> /* for rint */
#include <errno.h>
-#include <fltk/utf.h> /* for utf8encode */
-
#include "bw.h" /* for BrowserWindow */
#include "msg.h"
#include "binaryconst.h"
#include "colors.h"
+#include "utf8.hh"
#include "misc.h"
#include "uicmd.hh"
#include "history.h"
-#include "nav.h"
#include "menu.hh"
#include "prefs.h"
#include "capi.h"
@@ -49,7 +46,7 @@
#include "dw/ruler.hh"
/*-----------------------------------------------------------------------------
- * Defines
+ * Defines
*---------------------------------------------------------------------------*/
/* Define to 1 to ignore white space immediately after an open tag,
@@ -61,6 +58,7 @@
/*-----------------------------------------------------------------------------
* Name spaces
*---------------------------------------------------------------------------*/
+using namespace lout;
using namespace dw;
using namespace dw::core;
using namespace dw::core::ui;
@@ -106,24 +104,15 @@ static const char *Html_get_attr2(DilloHtml *html,
int tagsize,
const char *attrname,
int tag_parsing_flags);
-static void Html_add_widget(DilloHtml *html, Widget *widget,
- char *width_str, char *height_str,
- StyleAttrs *style_attrs);
static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof);
-static void Html_load_image(BrowserWindow *bw, DilloUrl *url,
- DilloImage *image);
+static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
+ const DilloUrl *requester, DilloImage *image);
static void Html_callback(int Op, CacheClient_t *Client);
-static int Html_tag_index(const char *tag);
static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx);
/*-----------------------------------------------------------------------------
* Local Data
*---------------------------------------------------------------------------*/
-/* The following array of font sizes has to be _strictly_ increasing */
-static const int FontSizes[] = {10, 12, 14, 18, 22, 28};
-static const int FontSizesNum = 6;
-static const int FontSizesBase = 2;
-
/* Parsing table structure */
typedef struct {
const char *name; /* element name */
@@ -210,7 +199,7 @@ static void Html_free(void *data)
/*
* Used by the "Load images" page menuitem.
- */
+ */
void a_Html_load_images(void *v_html, DilloUrl *pattern)
{
DilloHtml *html = (DilloHtml*)v_html;
@@ -219,6 +208,58 @@ void a_Html_load_images(void *v_html, DilloUrl *pattern)
}
/*
+ * Search for form
+ */
+static bool Html_contains_form(DilloHtml *html, void *v_form)
+{
+ for (int i = 0; i < html->forms->size(); i++) {
+ if (html->forms->get(i) == v_form) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Used by the "Submit form" form menuitem.
+ */
+void a_Html_form_submit(void *v_html, void *v_form)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_submit2(v_form);
+ }
+}
+
+/*
+ * Used by the "Reset form" form menuitem.
+ */
+void a_Html_form_reset(void *v_html, void *v_form)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_reset2(v_form);
+ }
+}
+
+/*
+ * Used by the "Show/Hide hiddens" form menuitem.
+ */
+void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display)
+{
+ DilloHtml *html = (DilloHtml*)v_html;
+
+ if (Html_contains_form(html, v_form)) {
+ /* it's still valid */
+ a_Html_form_display_hiddens2(v_form, (display != 0));
+ }
+}
+
+/*
* Set the URL data for image maps.
*/
static void Html_set_link_coordinates(DilloHtml *html, int link, int x, int y)
@@ -244,42 +285,20 @@ static int Html_set_new_link(DilloHtml *html, DilloUrl **url)
}
/*
- * Add a new image.
+ * Add a new image to our list.
+ * image is NULL if dillo will try to load the image immediately.
*/
-static int Html_add_new_linkimage(DilloHtml *html,
- DilloUrl **url, DilloImage *image)
+static void Html_add_new_htmlimage(DilloHtml *html,
+ DilloUrl **url, DilloImage *image)
{
- DilloLinkImage *li = dNew(DilloLinkImage, 1);
- li->url = *url;
- li->image = image;
+ DilloHtmlImage *hi = dNew(DilloHtmlImage, 1);
+ hi->url = *url;
+ hi->image = image;
+ a_Image_ref(image);
- int ni = html->images->size();
+ int n = html->images->size();
html->images->increase();
- html->images->set(ni, li);
- return ni;
-}
-
-/*
- * Set the font at the top of the stack. BImask specifies which
- * attributes in BI should be changed.
- */
-void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
- int BI, int BImask)
-{
- FontAttrs font_attrs;
-
- font_attrs = *S_TOP(html)->style->font;
- if (name)
- font_attrs.name = name;
- if (size)
- font_attrs.size = size;
- if (BImask & 1)
- font_attrs.weight = (BI & 1) ? 700 : 400;
- if (BImask & 2)
- font_attrs.style = (BI & 2) ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL;
-
- HTML_SET_TOP_ATTR (html, font,
- Font::create (HT2LT(html), &font_attrs));
+ html->images->set(n, hi);
}
/*
@@ -287,25 +306,26 @@ void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
* sets the style at the top of the stack.
*/
void a_Html_tag_set_align_attr(DilloHtml *html,
+ CssPropertyList *props,
const char *tag, int tagsize)
{
- const char *align, *charattr;
+ const char *align;
if ((align = a_Html_get_attr(html, tag, tagsize, "align"))) {
- Style *old_style = S_TOP(html)->style;
- StyleAttrs style_attrs = *old_style;
+ TextAlignType textAlignType = TEXT_ALIGN_LEFT;
if (dStrcasecmp (align, "left") == 0)
- style_attrs.textAlign = TEXT_ALIGN_LEFT;
+ textAlignType = TEXT_ALIGN_LEFT;
else if (dStrcasecmp (align, "right") == 0)
- style_attrs.textAlign = TEXT_ALIGN_RIGHT;
+ textAlignType = TEXT_ALIGN_RIGHT;
else if (dStrcasecmp (align, "center") == 0)
- style_attrs.textAlign = TEXT_ALIGN_CENTER;
+ textAlignType = TEXT_ALIGN_CENTER;
else if (dStrcasecmp (align, "justify") == 0)
- style_attrs.textAlign = TEXT_ALIGN_JUSTIFY;
+ textAlignType = TEXT_ALIGN_JUSTIFY;
+#if 0
else if (dStrcasecmp (align, "char") == 0) {
/* TODO: Actually not supported for <p> etc. */
- style_attrs.textAlign = TEXT_ALIGN_STRING;
+ v.textAlign = TEXT_ALIGN_STRING;
if ((charattr = a_Html_get_attr(html, tag, tagsize, "char"))) {
if (charattr[0] == 0)
/* TODO: ALIGN=" ", and even ALIGN="&32;" will reult in
@@ -319,8 +339,8 @@ void a_Html_tag_set_align_attr(DilloHtml *html,
/* TODO: Examine LANG attr of <html>. */
style_attrs.textAlignChar = '.';
}
- S_TOP(html)->style = Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+#endif
+ props->set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, textAlignType);
}
}
@@ -329,19 +349,22 @@ void a_Html_tag_set_align_attr(DilloHtml *html,
* sets the style in style_attrs. Returns true when set.
*/
bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag,
- int tagsize, StyleAttrs *style_attrs)
+ int tagsize, CssPropertyList *props)
{
const char *attr;
+ VAlignType valign;
if ((attr = a_Html_get_attr(html, tag, tagsize, "valign"))) {
if (dStrcasecmp (attr, "top") == 0)
- style_attrs->valign = VALIGN_TOP;
+ valign = VALIGN_TOP;
else if (dStrcasecmp (attr, "bottom") == 0)
- style_attrs->valign = VALIGN_BOTTOM;
+ valign = VALIGN_BOTTOM;
else if (dStrcasecmp (attr, "baseline") == 0)
- style_attrs->valign = VALIGN_BASELINE;
+ valign = VALIGN_BASELINE;
else
- style_attrs->valign = VALIGN_MIDDLE;
+ valign = VALIGN_MIDDLE;
+
+ props->set (CSS_PROPERTY_VERTICAL_ALIGN, CSS_TYPE_ENUM, valign);
return true;
} else
return false;
@@ -349,84 +372,17 @@ bool a_Html_tag_set_valign_attr(DilloHtml *html, const char *tag,
/*
- * Add a new DwPage into the current DwPage, for indentation.
- * left and right are the horizontal indentation amounts, space is the
- * vertical space around the block.
+ * Create and add a new Textblock to the current Textblock
*/
-static void Html_add_indented_widget(DilloHtml *html, Widget *textblock,
- int left, int right, int space)
+static void Html_add_textblock(DilloHtml *html, int space)
{
- StyleAttrs style_attrs;
- Style *style;
-
- style_attrs = *S_TOP(html)->style;
-
- style_attrs.margin.setVal (0);
- style_attrs.borderWidth.setVal (0);
- style_attrs.padding.setVal(0);
-
- /* Activate this for debugging */
-#if 0
- style_attrs.borderWidth.setVal (1);
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor());
- style_attrs.setBorderStyle (BORDER_DASHED);
-#endif
-
- style_attrs.margin.left = left;
- style_attrs.margin.right = right;
- style = Style::create (HT2LT(html), &style_attrs);
+ Textblock *textblock = new Textblock (prefs.limit_text_width);
- DW2TB(html->dw)->addParbreak (space, style);
- DW2TB(html->dw)->addWidget (textblock, style);
- DW2TB(html->dw)->addParbreak (space, style);
+ HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
+ HT2TB(html)->addWidget (textblock, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (space, html->styleEngine->wordStyle ());
S_TOP(html)->textblock = html->dw = textblock;
S_TOP(html)->hand_over_break = true;
- style->unref ();
-
- /* Handle it when the user clicks on a link */
- html->connectSignals(textblock);
-}
-
-/*
- * Create and add a new indented DwPage to the current DwPage
- */
-static void Html_add_indented(DilloHtml *html, int left, int right, int space)
-{
- Textblock *textblock = new Textblock (prefs.limit_text_width);
- Html_add_indented_widget (html, textblock, left, right, space);
-}
-
-/*
- * Given a font_size, this will return the correct 'level'.
- * (or the closest, if the exact level isn't found).
- */
-static int Html_fontsize_to_level(int fontsize)
-{
- int i, level;
- double normalized_size = fontsize / prefs.font_factor,
- approximation = FontSizes[FontSizesNum-1] + 1;
-
- for (i = level = 0; i < FontSizesNum; i++)
- if (approximation >= fabs(normalized_size - FontSizes[i])) {
- approximation = fabs(normalized_size - FontSizes[i]);
- level = i;
- } else {
- break;
- }
-
- return level;
-}
-
-/*
- * Given a level of a font, this will return the correct 'size'.
- */
-static int Html_level_to_fontsize(int level)
-{
- level = MAX(0, level);
- level = MIN(FontSizesNum - 1, level);
-
- return (int)rint(FontSizes[level]*prefs.font_factor);
}
/*
@@ -435,19 +391,19 @@ static int Html_level_to_fontsize(int level)
DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
const char *content_type)
{
- /* Init event receiver */
- linkReceiver.html = this;
-
/* Init main variables */
bw = p_bw;
page_url = a_Url_dup(url);
base_url = a_Url_dup(url);
dw = NULL;
+ /* Init event receiver */
+ linkReceiver.html = this;
+ HT2LT(this)->connectLink (&linkReceiver);
+
a_Bw_add_doc(p_bw, this);
/* Init for-parsing variables */
- Buf_Consumed = 0;
Start_Buf = NULL;
Start_Ofs = 0;
@@ -466,10 +422,13 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
DocType = DT_NONE; /* assume Tag Soup 0.0! :-) */
DocTypeVersion = 0.0f;
+ styleEngine = new StyleEngine (HT2LT (this));
+
+ cssUrls = new misc::SimpleVector <DilloUrl*> (1);
+
stack = new misc::SimpleVector <DilloHtmlState> (16);
stack->increase();
- stack->getRef(0)->style = NULL;
- stack->getRef(0)->table_cell_style = NULL;
+ stack->getRef(0)->table_cell_props = NULL;
stack->getRef(0)->parse_mode = DILLO_HTML_PARSE_MODE_INIT;
stack->getRef(0)->table_mode = DILLO_HTML_TABLE_MODE_NONE;
stack->getRef(0)->cell_text_align_set = false;
@@ -479,7 +438,6 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
stack->getRef(0)->textblock = NULL;
stack->getRef(0)->table = NULL;
stack->getRef(0)->ref_list_item = NULL;
- stack->getRef(0)->current_bg_color = prefs.bg_color;
stack->getRef(0)->hand_over_break = false;
InFlags = IN_NONE;
@@ -490,29 +448,24 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
pre_column = 0;
PreFirstChar = false;
PrevWasCR = false;
- PrevWasOpenTag = false;
- PrevWasSPC = false;
InVisitedLink = false;
ReqTagClose = false;
- CloseOneTag = false;
TagSoup = true;
- NameVal = NULL;
+ loadCssFromStash = false;
Num_HTML = Num_HEAD = Num_BODY = Num_TITLE = 0;
attr_data = dStr_sized_new(1024);
- parse_finished = false;
+ non_css_link_color = -1;
+ non_css_visited_color = -1;
+ visited_color = -1;
/* Init page-handling variables */
forms = new misc::SimpleVector <DilloHtmlForm*> (1);
inputs_outside_form = new misc::SimpleVector <DilloHtmlInput*> (1);
links = new misc::SimpleVector <DilloUrl*> (64);
- images = new misc::SimpleVector <DilloLinkImage*> (16);
- //a_Dw_image_map_list_init(&maps);
-
- link_color = prefs.link_color;
- visited_color = prefs.visited_color;
+ images = new misc::SimpleVector <DilloHtmlImage*> (16);
/* Initialize the main widget */
initDw();
@@ -521,34 +474,15 @@ DilloHtml::DilloHtml(BrowserWindow *p_bw, const DilloUrl *url,
}
/*
- * Miscelaneous initializations for Dw
+ * Miscellaneous initializations for Dw
*/
void DilloHtml::initDw()
{
- StyleAttrs style_attrs;
- FontAttrs font_attrs;
-
dReturn_if_fail (dw == NULL);
/* Create the main widget */
dw = stack->getRef(0)->textblock = new Textblock (prefs.limit_text_width);
- /* Create a dummy font, attribute, and tag for the bottom of the stack. */
- font_attrs.name = prefs.vw_fontname;
- font_attrs.size = Html_level_to_fontsize(FontSizesBase);
- font_attrs.weight = 400;
- font_attrs.style = FONT_STYLE_NORMAL;
-
- style_attrs.initValues ();
- style_attrs.font = Font::create (HT2LT(this), &font_attrs);
- style_attrs.color = Color::createSimple (HT2LT(this), prefs.text_color);
- stack->getRef(0)->style = Style::create (HT2LT(this), &style_attrs);
-
- stack->getRef(0)->table_cell_style = NULL;
-
- /* Handle it when the user clicks on a link */
- connectSignals(dw);
-
bw->num_page_bugs = 0;
dStr_truncate(bw->page_bugs, 0);
}
@@ -560,14 +494,17 @@ DilloHtml::~DilloHtml()
{
_MSG("::~DilloHtml(this=%p)\n", this);
- if (!parse_finished)
- freeParseData();
+ freeParseData();
a_Bw_remove_doc(bw, this);
a_Url_free(page_url);
a_Url_free(base_url);
+ for (int i = 0; i < cssUrls->size(); i++)
+ a_Url_free(cssUrls->get(i));
+ delete (cssUrls);
+
for (int i = 0; i < forms->size(); i++)
a_Html_form_delete (forms->get(i));
delete(forms);
@@ -577,28 +514,18 @@ DilloHtml::~DilloHtml()
delete(inputs_outside_form);
for (int i = 0; i < links->size(); i++)
- if (links->get(i))
- a_Url_free(links->get(i));
+ a_Url_free(links->get(i));
delete (links);
for (int i = 0; i < images->size(); i++) {
- DilloLinkImage *li = images->get(i);
- a_Url_free(li->url);
- if (li->image)
- a_Image_unref(li->image);
- dFree(li);
+ DilloHtmlImage *img = images->get(i);
+ a_Url_free(img->url);
+ a_Image_unref(img->image);
+ dFree(img);
}
delete (images);
- //a_Dw_image_map_list_free(&maps);
-}
-
-/*
- * Connect all signals of a textblock or an image.
- */
-void DilloHtml::connectSignals(Widget *dw)
-{
- dw->connectLink (&linkReceiver);
+ delete styleEngine;
}
/*
@@ -611,9 +538,19 @@ void DilloHtml::write(char *Buf, int BufSize, int Eof)
char *buf = Buf + Start_Ofs;
int bufsize = BufSize - Start_Ofs;
- dReturn_if_fail (dw != NULL);
+ _MSG("DilloHtml::write BufSize=%d Start_Ofs=%d\n", BufSize, Start_Ofs);
+#if 0
+ char *aux = dStrndup(Buf, BufSize);
+ MSG(" {%s}\n", aux);
+ dFree(aux);
+#endif
+ /* Update Start_Buf. It may be used after the parser is stopped */
Start_Buf = Buf;
+
+ dReturn_if (dw == NULL);
+ dReturn_if (stop_parser == true);
+
token_start = Html_write_raw(this, buf, bufsize, Eof);
Start_Ofs += token_start;
}
@@ -644,7 +581,9 @@ int DilloHtml::getCurTagLineNumber()
*/
void DilloHtml::freeParseData()
{
- (stack->getRef(0)->style)->unref (); /* template style */
+ for (int i = stack->size () - 1; i >= 0; i--)
+ if (stack->getRef (i)->table_cell_props)
+ stack->getRef (i)->table_cell_props->unref ();
delete(stack);
dStr_free(Stash, TRUE);
@@ -661,6 +600,8 @@ void DilloHtml::finishParsing(int ClientKey)
{
int si;
+ dReturn_if (stop_parser == true);
+
/* force the close of elements left open (TODO: not for XHTML) */
while ((si = stack->size() - 1)) {
if (stack->getRef(si)->tag_idx != -1) {
@@ -669,9 +610,6 @@ void DilloHtml::finishParsing(int ClientKey)
}
/* Remove this client from our active list */
a_Bw_close_client(bw, ClientKey);
-
- freeParseData();
- parse_finished = true;
}
/*
@@ -680,7 +618,10 @@ void DilloHtml::finishParsing(int ClientKey)
int DilloHtml::formNew(DilloHtmlMethod method, const DilloUrl *action,
DilloHtmlEnc enc, const char *charset)
{
- DilloHtmlForm *form = a_Html_form_new (this,method,action,enc,charset);
+ // avoid data loss on repush after CSS stylesheets have been loaded
+ bool enabled = bw->NumPendingStyleSheets == 0;
+ DilloHtmlForm *form = a_Html_form_new (this, method, action,
+ enc, charset, enabled);
int nf = forms->size ();
forms->increase ();
forms->set (nf, form);
@@ -713,16 +654,36 @@ void DilloHtml::loadImages (const DilloUrl *pattern)
{
dReturn_if_fail (bw->nav_expecting == FALSE);
+ /* If the user asked for a specific URL, the user (NULL) is the requester,
+ * but if the user just asked for all URLs, use the page URL as the
+ * requester. If the possible patterns become more complex, it might be
+ * good to have the caller supply the requester instead.
+ */
+ const DilloUrl *requester = pattern ? NULL : this->page_url;
+
for (int i = 0; i < images->size(); i++) {
if (images->get(i)->image) {
if ((!pattern) || (!a_Url_cmp(images->get(i)->url, pattern))) {
- Html_load_image(bw, images->get(i)->url, images->get(i)->image);
- images->get(i)->image = NULL; // web owns it now
+ if (Html_load_image(bw, images->get(i)->url, requester,
+ images->get(i)->image)) {
+ a_Image_unref (images->get(i)->image);
+ images->get(i)->image = NULL; // web owns it now
+ }
}
}
}
}
+/*
+ * Save URL in a vector (may be loaded later).
+ */
+void DilloHtml::addCssUrl(const DilloUrl *url)
+{
+ int nu = cssUrls->size();
+ cssUrls->increase();
+ cssUrls->set(nu, a_Url_dup(url));
+}
+
bool DilloHtml::HtmlLinkReceiver::enter (Widget *widget, int link, int img,
int x, int y)
{
@@ -732,10 +693,12 @@ bool DilloHtml::HtmlLinkReceiver::enter (Widget *widget, int link, int img,
if (link == -1) {
_MSG(" Link LEAVE notify...\n");
a_UIcmd_set_msg(bw, "");
+ a_UIcmd_set_pointer_on_link(bw, FALSE);
} else {
_MSG(" Link ENTER notify...\n");
Html_set_link_coordinates(html, link, x, y);
a_UIcmd_set_msg(bw, "%s", URL_STR(html->links->get(link)));
+ a_UIcmd_set_pointer_on_link(bw, TRUE);
}
return true;
}
@@ -758,14 +721,12 @@ bool DilloHtml::HtmlLinkReceiver::press (Widget *widget, int link, int img,
if (link != -1)
linkurl = html->links->get(link);
const bool_t loaded_img = (html->images->get(img)->image == NULL);
- a_UIcmd_image_popup(
- bw, html->images->get(img)->url, loaded_img, linkurl);
+ a_UIcmd_image_popup(bw, html->images->get(img)->url, loaded_img,
+ html->page_url, linkurl);
ret = true;
} else {
if (link == -1) {
- a_UIcmd_page_popup(bw, a_History_get_url(NAV_TOP_UIDX(bw)),
- bw->num_page_bugs != 0,
- html->unloadedImages());
+ a_UIcmd_page_popup(bw, bw->num_page_bugs != 0, html->cssUrls);
ret = true;
} else {
a_UIcmd_link_popup(bw, html->links->get(link));
@@ -786,22 +747,12 @@ bool DilloHtml::HtmlLinkReceiver::click (Widget *widget, int link, int img,
if ((img != -1) && (html->images->get(img)->image)) {
// clicked an image that has not already been loaded
- DilloUrl *pattern;
-
if (event->button == 1){
// load all instances of this image
- pattern = html->images->get(img)->url;
- } else {
- if (event->button == 2){
- // load all images
- pattern = NULL;
- } else {
- return false;
- }
+ DilloUrl *pattern = html->images->get(img)->url;
+ html->loadImages(pattern);
+ return true;
}
-
- html->loadImages(pattern);
- return true;
}
if (link != -1) {
@@ -892,7 +843,7 @@ static const Ent_t Entities[NumEnt] = {
{"loz",022712}, {"lrm",020016}, {"lsaquo",020071},{"lsquo",020030},
{"lt",60}, {"macr",0257}, {"mdash",020024},{"micro",0265},
{"middot",0267},{"minus",021022},{"mu",01674}, {"nabla",021007},
- {"nbsp",32}, {"ndash",020023},{"ne",021140}, {"ni",021013},
+ {"nbsp",0240}, {"ndash",020023},{"ne",021140}, {"ni",021013},
{"not",0254}, {"notin",021011},{"nsub",021204}, {"ntilde",0361},
{"nu",01675}, {"oacute",0363}, {"ocirc",0364}, {"oelig",0523},
{"ograve",0362},{"oline",020076},{"omega",01711}, {"omicron",01677},
@@ -952,14 +903,14 @@ static int Html_ms_stupid_quotes_2ucs(int isocode)
{
int ret;
switch (isocode) {
- case 145:
- case 146: ret = '\''; break;
- case 147:
- case 148: ret = '"'; break;
- case 149: ret = 176; break;
- case 150:
- case 151: ret = '-'; break;
- default: ret = isocode; break;
+ case 145:
+ case 146: ret = '\''; break;
+ case 147:
+ case 148: ret = '"'; break;
+ case 149: ret = 176; break;
+ case 150:
+ case 151: ret = '-'; break;
+ default: ret = isocode; break;
}
return ret;
}
@@ -991,7 +942,7 @@ static int Html_parse_entity(DilloHtml *html, const char *token,
/* strtol with base 16 accepts leading "0x" - we don't */
if (*s == '0' && s[1] == 'x') {
s++;
- isocode = 0;
+ isocode = 0;
} else {
isocode = strtol(s, &s, 16);
}
@@ -1015,7 +966,7 @@ static int Html_parse_entity(DilloHtml *html, const char *token,
} else if (isalpha(*s)) {
/* character entity reference */
- while (*++s && (isalnum(*s) || strchr(":_.-", *s)));
+ while (*++s && (isalnum(*s) || strchr(":_.-", *s))) ;
c = *s;
*s = 0;
@@ -1066,7 +1017,7 @@ char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
toksize-i, &entsize)) >= 0) {
if (isocode >= 128) {
/* multibyte encoding */
- n = utf8encode(isocode, buf);
+ n = a_Utf8_encode(isocode, buf);
for (k = 0; k < n; ++k)
new_str[j++] = buf[k];
} else {
@@ -1082,9 +1033,32 @@ char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize)
}
/*
+ * For white-space: pre-line, we must break the line if encountering a newline.
+ * Otherwise, collapse whitespace as usual.
+ */
+static void Html_process_space_pre_line(DilloHtml *html, const char *space,
+ int spacesize)
+{
+ int i, breakCnt = 0;
+
+ for (i = 0; i < spacesize; i++) {
+ /* Support for "\r", "\n" and "\r\n" line breaks */
+ if (space[i] == '\r' || (space[i] == '\n' && !html->PrevWasCR)) {
+ breakCnt++;
+ html->PrevWasCR = (space[i] == '\r');
+
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
+ }
+ }
+ if (breakCnt == 0) {
+ HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
+ }
+}
+
+/*
* Parse spaces
*/
-static void Html_process_space(DilloHtml *html, const char *space,
+static void Html_process_space(DilloHtml *html, const char *space,
int spacesize)
{
char *spc;
@@ -1108,11 +1082,12 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
spc = dStrnfill(spaceCnt, ' ');
- DW2TB(html->dw)->addText (spc, S_TOP(html)->style);
+ HT2TB(html)->addText (spc, spaceCnt,
+ html->styleEngine->wordStyle ());
dFree(spc);
spaceCnt = 0;
}
- DW2TB(html->dw)->addLinebreak (S_TOP(html)->style);
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
html->pre_column = 0;
}
html->PreFirstChar = false;
@@ -1140,16 +1115,18 @@ static void Html_process_space(DilloHtml *html, const char *space,
if (spaceCnt) {
spc = dStrnfill(spaceCnt, ' ');
- DW2TB(html->dw)->addText (spc, S_TOP(html)->style);
+ HT2TB(html)->addText (spc, spaceCnt, html->styleEngine->wordStyle ());
dFree(spc);
}
} else {
if (SGML_SPCDEL) {
- /* SGML_SPCDEL ignores white space inmediately after an open tag */
- } else if (!html->PrevWasSPC) {
- DW2TB(html->dw)->addSpace(S_TOP(html)->style);
- html->PrevWasSPC = true;
+ /* SGML_SPCDEL ignores white space immediately after an open tag */
+ } else if (html->styleEngine->wordStyle ()->whiteSpace ==
+ WHITE_SPACE_PRE_LINE) {
+ Html_process_space_pre_line(html, space, spacesize);
+ } else {
+ HT2TB(html)->addSpace(html->styleEngine->wordStyle ());
}
if (parse_mode == DILLO_HTML_PARSE_MODE_STASH_AND_BODY)
@@ -1168,7 +1145,7 @@ static void Html_process_space(DilloHtml *html, const char *space,
static void Html_process_word(DilloHtml *html, const char *word, int size)
{
int i, j, start;
- char *Pword, ch;
+ char *Pword;
DilloHtmlParseMode parse_mode = S_TOP(html)->parse_mode;
if (parse_mode == DILLO_HTML_PARSE_MODE_STASH ||
@@ -1195,39 +1172,66 @@ static void Html_process_word(DilloHtml *html, const char *word, int size)
Pword = a_Html_parse_entities(html, word, size);
for (start = i = 0; Pword[i]; start = i)
if (isspace(Pword[i])) {
- while (Pword[++i] && isspace(Pword[i]));
+ while (Pword[++i] && isspace(Pword[i])) ;
Html_process_space(html, Pword + start, i - start);
} else {
- while (Pword[++i] && !isspace(Pword[i]));
- ch = Pword[i];
- Pword[i] = 0;
- DW2TB(html->dw)->addText(Pword, S_TOP(html)->style);
- Pword[i] = ch;
+ while (Pword[++i] && !isspace(Pword[i])) ;
+ HT2TB(html)->addText(Pword + start, i - start,
+ html->styleEngine->wordStyle ());
html->pre_column += i - start;
html->PreFirstChar = false;
}
dFree(Pword);
} else {
+ const char *word2, *word2_end;
+
+ Pword = NULL;
if (!memchr(word,'&', size)) {
/* No entities */
- DW2TB(html->dw)->addText(word, S_TOP(html)->style);
+ word2 = word;
+ word2_end = word + size - 1;
} else {
/* Collapse white-space entities inside the word (except &nbsp;) */
Pword = a_Html_parse_entities(html, word, size);
- for (i = 0; Pword[i]; ++i)
- if (strchr("\t\f\n\r", Pword[i]))
- for (j = i; (Pword[j] = Pword[j+1]); ++j);
-
- DW2TB(html->dw)->addText(Pword, S_TOP(html)->style);
- dFree(Pword);
+ /* Collapse adjacent " \t\f\n\r" characters into a single space */
+ for (i = j = 0; (Pword[i] = Pword[j]); ++i, ++j) {
+ if (strchr(" \t\f\n\r", Pword[i])) {
+ if (i == 0 || (i > 0 && Pword[i-1] != ' '))
+ Pword[i] = ' ';
+ else
+ for (--i; Pword[j+1] && strchr(" \t\f\n\r", Pword[j+1]); ++j)
+ ;
+ }
+ }
+ word2 = Pword;
+ word2_end = word2 + strlen(word2) - 1;
+ }
+ for (start = i = 0; word2[i]; start = i) {
+ int len;
+
+ if (isspace(word2[i])) {
+ while (word2[++i] && isspace(word2[i])) ;
+ Html_process_space(html, word2 + start, i - start);
+ } else if (!strncmp(word2+i, utf8_zero_width_space, 3)) {
+ i += 3;
+ } else if (a_Utf8_ideographic(word2+i, word2_end, &len)) {
+ i += len;
+ HT2TB(html)->addText(word2 + start, i - start,
+ html->styleEngine->wordStyle ());
+ } else {
+ do {
+ i += len;
+ } while (word2[i] && !isspace(word2[i]) &&
+ strncmp(word2+i, utf8_zero_width_space, 3) &&
+ (!a_Utf8_ideographic(word2+i, word2_end, &len)));
+ HT2TB(html)->addText(word2 + start, i - start,
+ html->styleEngine->wordStyle ());
+ }
}
+ if (Pword == word2)
+ dFree(Pword);
}
-
- html->PrevWasOpenTag = false;
- html->PrevWasSPC = false;
- if (html->InFlags & IN_LI)
- html->WordAfterLI = true;
}
/*
@@ -1250,14 +1254,14 @@ static bool Html_match_tag(const char *tagstr, char *tag, int tagsize)
/*
* This function is called after popping the stack, to
- * handle nested DwPage widgets.
+ * handle nested Textblock widgets.
*/
static void Html_eventually_pop_dw(DilloHtml *html, bool hand_over_break)
{
if (html->dw != S_TOP(html)->textblock) {
if (hand_over_break)
- DW2TB(html->dw)->handOverBreak (S_TOP(html)->style);
- DW2TB(html->dw)->flush ();
+ HT2TB(html)->handOverBreak (html->styleEngine->style ());
+ HT2TB(html)->flush ();
html->dw = S_TOP(html)->textblock;
}
}
@@ -1275,10 +1279,8 @@ static void Html_push_tag(DilloHtml *html, int tag_idx)
* instead of copying all fields except for tag. --Jcid */
*html->stack->getRef(n_items) = *html->stack->getRef(n_items - 1);
html->stack->getRef(n_items)->tag_idx = tag_idx;
- /* proper memory management, may be unref'd later */
- (S_TOP(html)->style)->ref ();
- if (S_TOP(html)->table_cell_style)
- (S_TOP(html)->table_cell_style)->ref ();
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->ref ();
html->dw = S_TOP(html)->textblock;
}
@@ -1288,6 +1290,7 @@ static void Html_push_tag(DilloHtml *html, int tag_idx)
*/
static void Html_force_push_tag(DilloHtml *html, int tag_idx)
{
+ html->styleEngine->startElement (tag_idx);
Html_push_tag(html, tag_idx);
}
@@ -1298,15 +1301,32 @@ static void Html_real_pop_tag(DilloHtml *html)
{
bool hand_over_break;
- (S_TOP(html)->style)->unref ();
- if (S_TOP(html)->table_cell_style)
- (S_TOP(html)->table_cell_style)->unref ();
+ html->styleEngine->endElement (S_TOP(html)->tag_idx);
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->unref ();
hand_over_break = S_TOP(html)->hand_over_break;
html->stack->setSize (html->stack->size() - 1);
Html_eventually_pop_dw(html, hand_over_break);
}
/*
+ * Cleanup the stack to a given index.
+ */
+static void Html_tag_cleanup_to_idx(DilloHtml *html, int idx)
+{
+ int s_sz;
+ while ((s_sz = html->stack->size()) > idx) {
+ int toptag_idx = S_TOP(html)->tag_idx;
+ TagInfo toptag = Tags[toptag_idx];
+ if (s_sz > idx + 1 && toptag.EndTag != 'O')
+ BUG_MSG(" - forcing close of open tag: <%s>\n", toptag.name);
+ _MSG("Close: %*s%s\n", size," ", toptag.name);
+ toptag.close(html, toptag_idx);
+ Html_real_pop_tag(html);
+ }
+}
+
+/*
* Default close function for tags.
* (conditional cleanup of the stack)
* There are several ways of doing it. Considering the HTML 4.01 spec
@@ -1321,77 +1341,51 @@ static void Html_real_pop_tag(DilloHtml *html)
* 2.- If it exists, clean all the tags in between.
* 3.- Cleanup the matching tag. (on error, give a warning message)
*/
-static void Html_tag_cleanup_at_close(DilloHtml *html, int TagIdx)
+static void Html_tag_cleanup_at_close(DilloHtml *html, int new_idx)
{
int w3c_mode = !prefs.w3c_plus_heuristics;
- int stack_idx, cmp = 1;
- int new_idx = TagIdx;
-
- if (html->CloseOneTag) {
- Html_real_pop_tag(html);
- html->CloseOneTag = false;
- return;
- }
+ int stack_idx, tag_idx, matched = 0, expected = 0;
+ TagInfo new_tag = Tags[new_idx];
/* Look for the candidate tag to close */
- stack_idx = html->stack->size() - 1;
- while (stack_idx &&
- (cmp = (new_idx != html->stack->getRef(stack_idx)->tag_idx)) &&
- ((w3c_mode &&
- Tags[html->stack->getRef(stack_idx)->tag_idx].EndTag == 'O') ||
- (!w3c_mode &&
- (Tags[html->stack->getRef(stack_idx)->tag_idx].EndTag == 'O') ||
- Tags[html->stack->getRef(stack_idx)->tag_idx].TagLevel <
- Tags[new_idx].TagLevel))) {
- --stack_idx;
- }
-
- /* clean, up to the matching tag */
- if (cmp == 0 && stack_idx > 0) {
- /* There's a valid matching tag in the stack */
- while (html->stack->size() > stack_idx) {
- int toptag_idx = S_TOP(html)->tag_idx;
- /* Warn when we decide to close an open tag (for !w3c_mode) */
- if (html->stack->size() > stack_idx + 1 &&
- Tags[toptag_idx].EndTag != 'O')
- BUG_MSG(" - forcing close of open tag: <%s>\n",
- Tags[toptag_idx].name);
-
- /* Close this and only this tag */
- html->CloseOneTag = true;
- Tags[toptag_idx].close (html, toptag_idx);
+ stack_idx = html->stack->size();
+ while (--stack_idx) {
+ tag_idx = html->stack->getRef(stack_idx)->tag_idx;
+ if (tag_idx == new_idx) {
+ /* matching tag found */
+ matched = 1;
+ break;
+ } else if (Tags[tag_idx].EndTag == 'O') {
+ /* skip an optional tag */
+ continue;
+ } else if (w3c_mode || Tags[tag_idx].TagLevel >= new_tag.TagLevel) {
+ /* this is the tag that should have been closed */
+ expected = 1;
+ break;
}
+ }
+ if (matched) {
+ Html_tag_cleanup_to_idx(html, stack_idx);
+ } else if (expected) {
+ BUG_MSG("unexpected closing tag: </%s> -- expected </%s>.\n",
+ new_tag.name, Tags[tag_idx].name);
} else {
- if (stack_idx == 0) {
- BUG_MSG("unexpected closing tag: </%s>.\n", Tags[new_idx].name);
- } else {
- BUG_MSG("unexpected closing tag: </%s>. -- expected </%s>\n",
- Tags[new_idx].name,
- Tags[html->stack->getRef(stack_idx)->tag_idx].name);
- }
+ BUG_MSG("unexpected closing tag: </%s>.\n", new_tag.name);
}
}
/*
- * Cleanup (conditional), and Pop the tag (if it matches)
- */
-void a_Html_pop_tag(DilloHtml *html, int TagIdx)
-{
- Html_tag_cleanup_at_close(html, TagIdx);
-}
-
-/*
* Some parsing routines.
*/
/*
* Used by a_Html_parse_length
*/
-static Length Html_parse_length_or_multi_length (const char *attr,
- char **endptr)
+static CssLength Html_parse_length_or_multi_length (const char *attr,
+ char **endptr)
{
- Length l;
+ CssLength l;
double v;
char *end;
@@ -1399,12 +1393,12 @@ static Length Html_parse_length_or_multi_length (const char *attr,
switch (*end) {
case '%':
end++;
- l = createPerLength (v / 100);
+ l = CSS_CREATE_LENGTH (v / 100, CSS_LENGTH_TYPE_PERCENTAGE);
break;
case '*':
end++;
- l = createRelLength (v);
+ l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_RELATIVE);
break;
/*
The "px" suffix seems not allowed by HTML4.01 SPEC.
@@ -1413,7 +1407,7 @@ static Length Html_parse_length_or_multi_length (const char *attr,
end += 2;
*/
default:
- l = createAbsLength ((int)v);
+ l = CSS_CREATE_LENGTH (v, CSS_LENGTH_TYPE_PX);
break;
}
@@ -1427,24 +1421,24 @@ static Length Html_parse_length_or_multi_length (const char *attr,
* Returns a length or a percentage, or UNDEF_LENGTH in case
* of an error, or if attr is NULL.
*/
-Length a_Html_parse_length (DilloHtml *html, const char *attr)
+CssLength a_Html_parse_length (DilloHtml *html, const char *attr)
{
- Length l;
+ CssLength l;
char *end;
l = Html_parse_length_or_multi_length (attr, &end);
- if (isRelLength (l))
+ if (CSS_LENGTH_TYPE (l) == CSS_LENGTH_TYPE_RELATIVE)
/* not allowed as &Length; */
- return LENGTH_AUTO;
+ l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
else {
/* allow only whitespaces */
if (*end && !isspace (*end)) {
BUG_MSG("Garbage after length: %s\n", attr);
- return LENGTH_AUTO;
+ l = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
}
}
- _MSG("a_Html_parse_length: \"%s\" %d\n", attr, absLengthVal(l));
+ _MSG("a_Html_parse_length: \"%s\" %d\n", attr, CSS_LENGTH_VALUE(l));
return l;
}
@@ -1498,7 +1492,7 @@ static int
* recognizes the "HTML Level" with or without the URL. The convention
* comes from mozilla (see URLs below), but Dillo doesn't have the same
* rendering modes, so it may be better to chose another behaviour. --Jcid
- *
+ *
* http://www.mozilla.org/docs/web-developer/quirks/doctypes.html
* http://lists.auriga.wearlab.de/pipermail/dillo-dev/2004-October/002300.html
*
@@ -1507,7 +1501,7 @@ static int
static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
{
static const char HTML_sig [] = "<!DOCTYPE HTML PUBLIC ";
- static const char HTML20 [] = "-//IETF//DTD HTML//EN";
+ static const char HTML20 [] = "-//IETF//DTD HTML 2.0";
static const char HTML32 [] = "-//W3C//DTD HTML 3.2";
static const char HTML40 [] = "-//W3C//DTD HTML 4.0";
static const char HTML401 [] = "-//W3C//DTD HTML 4.01";
@@ -1524,9 +1518,9 @@ static void Html_parse_doctype(DilloHtml *html, const char *tag, int tagsize)
* and replace '\n' and '\r' with ' ' inside quoted strings. */
for (i = 0, p = ntag; *p; ++p) {
if (isspace(*p)) {
- for (ntag[i++] = ' '; isspace(p[1]); ++p);
+ for (ntag[i++] = ' '; isspace(p[1]); ++p) ;
} else if ((quote = *p) == '"' || *p == '\'') {
- for (ntag[i++] = *p++; (ntag[i++] = *p) && *p != quote; ++p) {
+ for (ntag[i++] = *p++; (ntag[i] = *p) && ntag[i++] != quote; ++p) {
if (*p == '\n' || *p == '\r')
ntag[i - 1] = ' ';
p += (p[0] == '\r' && p[1] == '\n') ? 1 : 0;
@@ -1595,7 +1589,6 @@ static void Html_tag_close_html(DilloHtml *html, int TagIdx)
/* beware of pages with multiple HTML close tags... :-P */
html->InFlags &= ~IN_HTML;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1622,16 +1615,22 @@ static void Html_tag_open_head(DilloHtml *html, const char *tag, int tagsize)
* Handle close HEAD element
* Note: as a side effect of Html_test_section() this function is called
* twice when the head element is closed implicitly.
+ * Note2: HEAD is parsed once completely got.
*/
static void Html_tag_close_head(DilloHtml *html, int TagIdx)
{
if (html->InFlags & IN_HEAD) {
+ _MSG("Closing HEAD section\n");
if (html->Num_TITLE == 0)
BUG_MSG("HEAD section lacks the TITLE element\n");
-
+
html->InFlags &= ~IN_HEAD;
+
+ /* charset is already set, load remote stylesheets now */
+ for (int i = 0; i < html->cssUrls->size(); i++) {
+ a_Html_load_stylesheet(html, html->cssUrls->get(i));
+ }
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1653,11 +1652,10 @@ static void Html_tag_close_title(DilloHtml *html, int TagIdx)
if (html->InFlags & IN_HEAD) {
/* title is only valid inside HEAD */
a_UIcmd_set_page_title(html->bw, html->Stash->str);
- a_History_set_title(NAV_TOP_UIDX(html->bw),html->Stash->str);
+ a_History_set_title_by_url(html->page_url, html->Stash->str);
} else {
BUG_MSG("the TITLE element must be inside the HEAD section\n");
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1677,16 +1675,33 @@ static void Html_tag_open_script(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_script(DilloHtml *html, int TagIdx)
{
/* eventually the stash will be sent to an interpreter for parsing */
- a_Html_pop_tag(html, TagIdx);
}
/*
* Handle open STYLE
- * store the contents to the stash where (in the future) the style
- * sheet interpreter can get it.
+ * Store contents in the stash where the style sheet interpreter can get it.
*/
static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
{
+ const char *attrbuf;
+
+ html->loadCssFromStash = true;
+
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ BUG_MSG("type attribute is required for <style>\n");
+ } else if (dStrcasecmp(attrbuf, "text/css")) {
+ html->loadCssFromStash = false;
+ }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
+ dStrcasecmp(attrbuf, "all") && !dStristr(attrbuf, "screen")) {
+ /* HTML 4.01 sec. 6.13 says that media descriptors are case-sensitive,
+ * but sec. 14.2.3 says that the attribute is case-insensitive.
+ * TODO can be a comma-separated list.
+ * TODO handheld.
+ */
+ html->loadCssFromStash = false;
+ }
+
a_Html_stash_init(html);
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_VERBATIM;
}
@@ -1696,8 +1711,9 @@ static void Html_tag_open_style(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_close_style(DilloHtml *html, int TagIdx)
{
- /* eventually the stash will be sent to an interpreter for parsing */
- a_Html_pop_tag(html, TagIdx);
+ if (prefs.parse_embedded_css && html->loadCssFromStash)
+ html->styleEngine->parse(html, NULL, html->Stash->str, html->Stash->len,
+ CSS_ORIGIN_AUTHOR);
}
/*
@@ -1707,9 +1723,9 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
Textblock *textblock;
- StyleAttrs style_attrs;
- Style *style;
+ CssPropertyList props;
int32_t color;
+ int tag_index_a = a_Html_tag_index ("a");
if (!(html->InFlags & IN_BODY))
html->InFlags |= IN_BODY;
@@ -1724,43 +1740,53 @@ static void Html_tag_open_body(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("unclosed HEAD element\n");
}
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
- if (!prefs.force_my_colors) {
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
- color = a_Html_color_parse(html, attrbuf, prefs.bg_color);
- if (color == 0xffffff && !prefs.allow_white_bg)
- color = prefs.bg_color;
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ color = a_Html_color_parse(html, attrbuf, -1);
+ if (color != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, color);
+ }
- style_attrs = *html->dw->getStyle ();
- style_attrs.backgroundColor = Color::createShaded(HT2LT(html), color);
- style = Style::create (HT2LT(html), &style_attrs);
- html->dw->setStyle (style);
- style->unref ();
- S_TOP(html)->current_bg_color = color;
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) {
+ color = a_Html_color_parse(html, attrbuf, -1);
+ if (color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR, color);
+ }
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "text"))) {
- color = a_Html_color_parse(html, attrbuf, prefs.text_color);
- HTML_SET_TOP_ATTR (html, color,
- Color::createSimple (HT2LT(html),color));
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link")))
+ html->non_css_link_color = a_Html_color_parse(html, attrbuf, -1);
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "link")))
- html->link_color = a_Html_color_parse(html,attrbuf,prefs.link_color);
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink")))
+ html->non_css_visited_color = a_Html_color_parse(html, attrbuf, -1);
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vlink")))
- html->visited_color = a_Html_color_parse(html, attrbuf,
- prefs.visited_color);
+ html->styleEngine->setNonCssHints (&props);
+ html->dw->setStyle (html->styleEngine->style ());
- if (prefs.contrast_visited_color) {
- /* get a color that has a "safe distance" from text, link and bg */
- html->visited_color =
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->current_bg_color);
- }
+ /* Determine a color for visited links.
+ * This color is computed once per page and used for immediate feedback
+ * when clicking a link.
+ * On reload style including color for visited links is computed properly
+ * according to CSS.
+ */
+ html->styleEngine->startElement (tag_index_a);
+ html->styleEngine->setPseudoVisited ();
+ if (html->non_css_visited_color != -1) {
+ CssPropertyList vprops;
+ vprops.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_visited_color);
+ html->styleEngine->setNonCssHints (&vprops);
+ }
+ html->visited_color = html->styleEngine->style ()->color->getColor ();
+ html->styleEngine->endElement (tag_index_a);
+
+ if (prefs.contrast_visited_color) {
+ /* get a color that has a "safe distance" from text, link and bg */
+ html->visited_color =
+ a_Color_vc(html->visited_color,
+ html->styleEngine->style ()->color->getColor(),
+ html->non_css_link_color,
+ html->styleEngine->backgroundStyle()->backgroundColor->getColor());
}
S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_BODY;
@@ -1775,7 +1801,6 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx)
/* some tag soup pages use multiple BODY tags... */
html->InFlags &= ~IN_BODY;
}
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -1785,13 +1810,12 @@ static void Html_tag_close_body(DilloHtml *html, int TagIdx)
*/
static void Html_tag_open_p(DilloHtml *html, const char *tag, int tagsize)
{
- if ((html->InFlags & IN_LI) && !html->WordAfterLI) {
- /* ignore first parbreak after an empty <LI> */
- html->WordAfterLI = true;
- } else {
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- }
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ CssPropertyList props;
+
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->inheritBackgroundColor ();
+ html->styleEngine->setNonCssHints (&props);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -1805,11 +1829,10 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
char *src;
DilloUrl *url;
Textblock *textblock;
- StyleAttrs style_attrs;
- Style *link_style;
Widget *bullet;
+ CssPropertyList props;
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "src")))
return;
@@ -1819,46 +1842,40 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
src = dStrdup(attrbuf);
- style_attrs = *(S_TOP(html)->style);
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) { /* visited frame */
- style_attrs.color =
- Color::createSimple (HT2LT(html), html->visited_color);
- } else { /* unvisited frame */
- style_attrs.color = Color::createSimple (HT2LT(html), html->link_color);
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
+ /* visited frame */
+ html->styleEngine->setPseudoVisited ();
+ } else {
+ /* unvisited frame */
+ html->styleEngine->setPseudoLink ();
}
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
- link_style = Style::create (HT2LT(html), &style_attrs);
- textblock->addParbreak (5, S_TOP(html)->style);
+ props.set (PROPERTY_X_LINK, CSS_TYPE_INTEGER, Html_set_new_link(html,&url));
+ html->styleEngine->setNonCssHints (&props);
+
+ textblock->addParbreak (5, html->styleEngine->wordStyle ());
- /* The bullet will be assigned the current list style, which should
- * be "disc" by default, but may in very weird pages be different.
- * Anyway, there should be no harm. */
bullet = new Bullet();
- textblock->addWidget(bullet, S_TOP(html)->style);
- textblock->addSpace(S_TOP(html)->style);
+ textblock->addWidget(bullet, html->styleEngine->wordStyle ());
+ textblock->addSpace(html->styleEngine->wordStyle ());
if (tolower(tag[1]) == 'i') {
/* IFRAME usually comes with very long advertising/spying URLS,
* to not break rendering we will force name="IFRAME" */
- textblock->addText ("IFRAME", link_style);
+ textblock->addText ("IFRAME", html->styleEngine->wordStyle ());
} else {
/* FRAME:
* If 'name' tag is present use it, if not use 'src' value */
if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
- textblock->addText (src, link_style);
+ textblock->addText (src, html->styleEngine->wordStyle ());
} else {
- textblock->addText (attrbuf, link_style);
+ textblock->addText (attrbuf, html->styleEngine->wordStyle ());
}
}
- textblock->addParbreak (5, S_TOP(html)->style);
+ textblock->addParbreak (5, html->styleEngine->wordStyle ());
- link_style->unref ();
dFree(src);
}
@@ -1870,9 +1887,9 @@ static void Html_tag_open_frame (DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_frameset (DilloHtml *html,
const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- DW2TB(html->dw)->addText("--FRAME--", S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 5);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
+ HT2TB(html)->addText("--FRAME--", html->styleEngine->wordStyle ());
+ Html_add_textblock(html, 5);
}
/*
@@ -1880,53 +1897,26 @@ static void Html_tag_open_frameset (DilloHtml *html,
*/
static void Html_tag_open_h(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ CssPropertyList props;
+
- /* TODO: combining these two would be slightly faster */
- a_Html_set_top_font(html, prefs.vw_fontname,
- Html_level_to_fontsize(FontSizesNum - (tag[2] - '0')),
- 1, 3);
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ html->styleEngine->inheritBackgroundColor ();
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- /* First finalize unclosed H tags (we test if already named anyway) */
- a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
- a_Menu_pagemarks_add(html->bw, DW2TB(html->dw),
- S_TOP(html)->style, (tag[2] - '0'));
a_Html_stash_init(html);
S_TOP(html)->parse_mode =
DILLO_HTML_PARSE_MODE_STASH_AND_BODY;
}
/*
- * Handle close: <H1> | <H2> | <H3> | <H4> | <H5> | <H6>
- */
-static void Html_tag_close_h(DilloHtml *html, int TagIdx)
-{
- a_Menu_pagemarks_set_text(html->bw, html->Stash->str);
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
-}
-
-/*
- * <BIG> | <SMALL>
- */
-static void Html_tag_open_big_small(DilloHtml *html,
- const char *tag, int tagsize)
-{
- int level;
-
- level =
- Html_fontsize_to_level(S_TOP(html)->style->font->size) +
- ((dStrncasecmp(tag+1, "big", 3)) ? -1 : 1);
- a_Html_set_top_font(html, NULL, Html_level_to_fontsize(level), 0, 0);
-}
-
-/*
* <BR>
*/
static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addLinebreak (S_TOP(html)->style);
+ HT2TB(html)->addLinebreak (html->styleEngine->wordStyle ());
}
/*
@@ -1934,39 +1924,29 @@ static void Html_tag_open_br(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *old_style;
- /*Font font;*/
const char *attrbuf;
+ char *fontFamily = NULL;
int32_t color;
+ CssPropertyList props;
- if (!prefs.force_my_colors) {
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
-
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) {
- if (prefs.contrast_visited_color && html->InVisitedLink) {
- color = html->visited_color;
- } else {
- /* use the tag-specified color */
- color = a_Html_color_parse(html, attrbuf,
- style_attrs.color->getColor());
- style_attrs.color = Color::createSimple (HT2LT(html), color);
- }
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "color"))) {
+ if (prefs.contrast_visited_color && html->InVisitedLink) {
+ color = html->visited_color;
+ } else {
+ /* use the tag-specified color */
+ color = a_Html_color_parse(html, attrbuf, -1);
}
+ if (color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR, color);
+ }
-#if 0
- //if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) {
- // font = *( style_attrs.font );
- // font.name = attrbuf;
- // style_attrs.font = a_Dw_style_font_new_from_list (&font);
- //}
-#endif
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "face"))) {
+ fontFamily = dStrdup(attrbuf);
+ props.set (CSS_PROPERTY_FONT_FAMILY, CSS_TYPE_SYMBOL, fontFamily);
}
+
+ html->styleEngine->setNonCssHints (&props);
+ dFree(fontFamily);
}
/*
@@ -1974,62 +1954,33 @@ static void Html_tag_open_font(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_abbr(DilloHtml *html, const char *tag, int tagsize)
{
-// DwTooltip *tooltip;
-// const char *attrbuf;
-//
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
-// tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-// HTML_SET_TOP_ATTR(html, x_tooltip, tooltip);
-// }
-}
-
-/*
- * <B>
- */
-static void Html_tag_open_b(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
-}
-
-/*
- * <STRONG>
- */
-static void Html_tag_open_strong(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
-}
+ const char *attrbuf;
-/*
- * <I>
- */
-static void Html_tag_open_i(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ CssPropertyList props;
+ char *tooltip_str = dStrdup(attrbuf);
-/*
- * <EM>
- */
-static void Html_tag_open_em(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+ }
}
/*
- * <CITE>
+ * <CENTER>
*/
-static void Html_tag_open_cite(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
}
/*
- * <CENTER>
+ * </CENTER>, also used for </TABLE>
*/
-static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_close_center(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- HTML_SET_TOP_ATTR(html, textAlign, TEXT_ALIGN_CENTER);
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
}
/*
@@ -2038,42 +1989,32 @@ static void Html_tag_open_center(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_address(DilloHtml *html,
const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
-
-/*
- * <TT>
- */
-static void Html_tag_open_tt(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
- * Read image-associated tag attributes,
- * create new image and add it to the html page (if add is TRUE).
+ * Read image-associated tag attributes and create new image.
*/
-DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
- int tagsize, DilloUrl *url,
- dw::core::style::StyleAttrs *style_attrs,
- bool add)
+DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
+ int tagsize, DilloUrl *url)
{
- const int MAX_W = 6000, MAX_H = 6000;
-
DilloImage *Image;
char *width_ptr, *height_ptr, *alt_ptr;
const char *attrbuf;
- Length l_w, l_h;
- int space, w = 0, h = 0;
+ CssLength l_w = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ CssLength l_h = CSS_CREATE_LENGTH(0.0, CSS_LENGTH_TYPE_AUTO);
+ int space, border, w = 0, h = 0;
bool load_now;
+ CssPropertyList props;
+ char *tooltip_str = NULL;
-// if (prefs.show_tooltip &&
-// (attrbuf = a_Html_get_attr(html, tag, tagsize, "title")))
-// style_attrs->x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ tooltip_str = dStrdup(attrbuf);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
+ }
alt_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "alt", NULL);
- if ((!alt_ptr || !*alt_ptr) && !a_UIcmd_get_images_enabled(html->bw)) {
+ if ((!alt_ptr || !*alt_ptr) && !prefs.load_images) {
dFree(alt_ptr);
alt_ptr = dStrdup("[IMG]"); // Place holder for img_off mode
}
@@ -2083,17 +2024,35 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
// TODO: the same for percentage and relative lengths.
if (width_ptr) {
l_w = a_Html_parse_length (html, width_ptr);
- w = isAbsLength(l_w) ? absLengthVal(l_w) : 0;
+ w = (int) (CSS_LENGTH_TYPE(l_w) == CSS_LENGTH_TYPE_PX ?
+ CSS_LENGTH_VALUE(l_w) : 0);
}
if (height_ptr) {
l_h = a_Html_parse_length (html, height_ptr);
- h = isAbsLength(l_h) ? absLengthVal(l_h) : 0;
- }
- if (w < 0 || h < 0 || abs(w*h) > MAX_W * MAX_H) {
+ h = (int) (CSS_LENGTH_TYPE(l_h) == CSS_LENGTH_TYPE_PX ?
+ CSS_LENGTH_VALUE(l_h) : 0);
+ }
+ /* Check for suspicious image size request that would cause
+ * an excessive amount of memory to be allocated for the
+ * image buffer.
+ * Be careful to avoid integer overflows during the checks.
+ * There is an additional check in dw/image.cc to catch cases
+ * where only one dimension is given and the image is scaled
+ * preserving its original aspect ratio.
+ * Size requests passed via CSS are also checked there.
+ */
+ if (w < 0 || h < 0 ||
+ w > IMAGE_MAX_AREA || h > IMAGE_MAX_AREA ||
+ (h > 0 && w > IMAGE_MAX_AREA / h)) {
dFree(width_ptr);
dFree(height_ptr);
width_ptr = height_ptr = NULL;
- MSG("a_Html_add_new_image: suspicious image size request %dx%d\n", w, h);
+ MSG("a_Html_image_new: suspicious image size request %dx%d\n", w, h);
+ } else {
+ if (CSS_LENGTH_TYPE(l_w) != CSS_LENGTH_TYPE_AUTO)
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE, l_w);
+ if (CSS_LENGTH_TYPE(l_h) != CSS_LENGTH_TYPE_AUTO)
+ props.set (CSS_PROPERTY_HEIGHT, CSS_TYPE_LENGTH_PERCENTAGE, l_h);
}
/* TODO: we should scale the image respecting its ratio.
@@ -2106,34 +2065,72 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
/* Spacing to the left and right */
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "hspace"))) {
space = strtol(attrbuf, NULL, 10);
- if (space > 0)
- style_attrs->margin.left = style_attrs->margin.right = space;
+ if (space > 0) {
+ space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_MARGIN_LEFT, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ props.set (CSS_PROPERTY_MARGIN_RIGHT, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ }
}
/* Spacing at the top and bottom */
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "vspace"))) {
space = strtol(attrbuf, NULL, 10);
- if (space > 0)
- style_attrs->margin.top = style_attrs->margin.bottom = space;
+ if (space > 0) {
+ space = CSS_CREATE_LENGTH(space, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_MARGIN_TOP, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ props.set (CSS_PROPERTY_MARGIN_BOTTOM, CSS_TYPE_LENGTH_PERCENTAGE,
+ space);
+ }
+ }
+
+ /* Border */
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border"))) {
+ border = strtol(attrbuf, NULL, 10);
+ if (border >= 0) {
+ border = CSS_CREATE_LENGTH(border, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ border);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, border);
+
+ props.set (CSS_PROPERTY_BORDER_TOP_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_LEFT_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_STYLE, CSS_TYPE_ENUM,
+ BORDER_SOLID);
+ }
}
/* x_img is an index to a list of {url,image} pairs.
- * We know Html_add_new_linkimage() will use size() as its next index */
- style_attrs->x_img = html->images->size();
+ * We know Html_add_new_htmlimage() will use size() as its next index */
+ props.set (PROPERTY_X_IMG, CSS_TYPE_INTEGER, html->images->size());
- /* Add a new image widget to this page */
- Image = a_Image_new(0, 0, alt_ptr, S_TOP(html)->current_bg_color);
- if (add) {
- Html_add_widget(html, (Widget*)Image->dw, width_ptr, height_ptr,
- style_attrs);
- }
+ html->styleEngine->setNonCssHints(&props);
- load_now = a_UIcmd_get_images_enabled(html->bw) ||
- (a_Capi_get_flags(url) & CAPI_IsCached);
- Html_add_new_linkimage(html, &url, load_now ? NULL : Image);
+ /* Add a new image widget to this page */
+ Image = a_Image_new(alt_ptr, 0);
+ if (HT2TB(html)->getBgColor())
+ Image->bg_color = HT2TB(html)->getBgColor()->getColor();
+
+ load_now = prefs.load_images ||
+ !dStrcasecmp(URL_SCHEME(url), "data") ||
+ (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached);
+ bool loading = false;
if (load_now)
- Html_load_image(html->bw, url, Image);
+ loading = Html_load_image(html->bw, url, html->page_url, Image);
+ Html_add_new_htmlimage(html, &url, loading ? NULL : Image);
+ dFree(tooltip_str);
dFree(width_ptr);
dFree(height_ptr);
dFree(alt_ptr);
@@ -2143,21 +2140,23 @@ DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
/*
* Tell cache to retrieve image
*/
-static void Html_load_image(BrowserWindow *bw, DilloUrl *url,
- DilloImage *Image)
+static bool Html_load_image(BrowserWindow *bw, DilloUrl *url,
+ const DilloUrl *requester, DilloImage *Image)
{
DilloWeb *Web;
int ClientKey;
/* Fill a Web structure for the cache query */
- Web = a_Web_new(url);
+ Web = a_Web_new(url, requester);
Web->bw = bw;
Web->Image = Image;
+ a_Image_ref(Image);
Web->flags |= WEB_Image;
/* Request image data from the cache */
if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
a_Bw_add_client(bw, ClientKey, 0);
a_Bw_add_url(bw, url);
}
+ return ClientKey != 0;
}
/*
@@ -2170,9 +2169,7 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
DilloImage *Image;
DilloUrl *url, *usemap_url;
Textblock *textblock;
- StyleAttrs style_attrs;
const char *attrbuf;
- int border;
/* This avoids loading images. Useful for viewing suspicious HTML email. */
if (URL_FLAGS(html->base_url) & URL_SpamSafe)
@@ -2182,41 +2179,21 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
!(url = a_Html_url_new(html, attrbuf, NULL, 0)))
return;
- textblock = DW2TB(html->dw);
+ textblock = HT2TB(html);
usemap_url = NULL;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "usemap")))
/* TODO: usemap URLs outside of the document are not used. */
usemap_url = a_Html_url_new(html, attrbuf, NULL, 0);
- /* Set the style attributes for this image */
- style_attrs = *S_TOP(html)->style;
- if (S_TOP(html)->style->x_link != -1 ||
- usemap_url != NULL) {
- /* Images within links */
- border = 1;
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
- border = strtol (attrbuf, NULL, 10);
-
- if (S_TOP(html)->style->x_link != -1) {
- /* In this case we can use the text color */
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor()));
- } else {
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), html->link_color));
- }
- style_attrs.setBorderStyle (BORDER_SOLID);
- style_attrs.borderWidth.setVal (border);
- }
-
- Image = a_Html_add_new_image(html, tag, tagsize, url, &style_attrs, true);
+ Image = a_Html_image_new(html, tag, tagsize, url);
+ HT2TB(html)->addWidget((Widget*)Image->dw, html->styleEngine->style());
/* Image maps */
if (a_Html_get_attr(html, tag, tagsize, "ismap")) {
((::dw::Image*)Image->dw)->setIsMap();
_MSG(" Html_tag_open_img: server-side map (ISMAP)\n");
- } else if (S_TOP(html)->style->x_link != -1 &&
+ } else if (html->styleEngine->style ()->x_link != -1 &&
usemap_url == NULL) {
/* For simple links, we have to suppress the "image_pressed" signal.
* This is overridden for USEMAP images. */
@@ -2225,10 +2202,9 @@ static void Html_tag_open_img(DilloHtml *html, const char *tag, int tagsize)
if (usemap_url) {
((::dw::Image*)Image->dw)->setUseMap(&html->maps,
- new ::object::String(usemap_url->url_string->str));
+ new ::object::String(URL_STR(usemap_url)));
a_Url_free (usemap_url);
}
- html->connectSignals((Widget*)Image->dw);
}
/*
@@ -2244,13 +2220,15 @@ static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
BUG_MSG("nested <map>\n");
} else {
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
+ html->InFlags |= IN_MAP;
hash_name = dStrconcat("#", attrbuf, NULL);
url = a_Html_url_new(html, hash_name, NULL, 0);
- html->maps.startNewMap(new ::object::String(url->url_string->str));
+ html->maps.startNewMap(new ::object::String(URL_STR(url)));
a_Url_free (url);
dFree(hash_name);
+ } else {
+ BUG_MSG("name attribute is required for <map>\n");
}
- html->InFlags |= IN_MAP;
}
}
@@ -2259,8 +2237,18 @@ static void Html_tag_open_map(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_close_map(DilloHtml *html, int TagIdx)
{
+ /* This is a hack for the perhaps frivolous feature of drawing image map
+ * shapes when there is no image to display. If this map is defined after
+ * an image that has not been loaded (img != NULL), tell the image to
+ * redraw. (It will only do so if it uses a map.)
+ */
+ for (int i = 0; i < html->images->size(); i++) {
+ DilloImage *img = html->images->get(i)->image;
+
+ if (img)
+ ((dw::Image*) img->dw)->forceMapRedraw();
+ }
html->InFlags &= ~IN_MAP;
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2306,7 +2294,7 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
const char *attrbuf;
int link = -1;
Shape *shape = NULL;
-
+
if (!(html->InFlags & IN_MAP)) {
BUG_MSG("<area> element not inside <map>\n");
return;
@@ -2368,7 +2356,7 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
dReturn_if_fail ( url != NULL );
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "alt")))
a_Url_set_alt(url, attrbuf);
-
+
link = Html_set_new_link(html, &url);
}
if (type == BACKGROUND)
@@ -2384,45 +2372,30 @@ static void Html_tag_open_area(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_object(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *style;
DilloUrl *url, *base_url = NULL;
const char *attrbuf;
+ CssPropertyList props;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "codebase"))) {
base_url = a_Html_url_new(html, attrbuf, NULL, 0);
}
-
+
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "data"))) {
url = a_Html_url_new(html, attrbuf,
URL_STR(base_url), (base_url != NULL));
dReturn_if_fail ( url != NULL );
- style_attrs = *S_TOP(html)->style;
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) {
- style_attrs.color = Color::createSimple (
- HT2LT(html),
- html->visited_color
-/*
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->style->backgroundColor->getColor()),
-*/
- );
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
+ html->styleEngine->setPseudoVisited ();
} else {
- style_attrs.color = Color::createSimple(HT2LT(html),
- html->link_color);
+ html->styleEngine->setPseudoLink ();
}
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
+ props.set(PROPERTY_X_LINK, CSS_TYPE_INTEGER,
+ Html_set_new_link(html, &url));
+ html->styleEngine->setNonCssHints (&props);
- style = Style::create (HT2LT(html), &style_attrs);
- DW2TB(html->dw)->addText("[OBJECT]", style);
- style->unref ();
+ HT2TB(html)->addText("[OBJECT]", html->styleEngine->wordStyle ());
}
a_Url_free(base_url);
}
@@ -2456,7 +2429,7 @@ static const char* Html_get_javascript_link(DilloHtml *html)
static void Html_add_anchor(DilloHtml *html, const char *name)
{
_MSG("Registering ANCHOR: %s\n", name);
- if (!DW2TB(html->dw)->addAnchor (name, S_TOP(html)->style))
+ if (!HT2TB(html)->addAnchor (name, html->styleEngine->style ()))
BUG_MSG("Anchor names must be unique within the document\n");
/*
* According to Sec. 12.2.1 of the HTML 4.01 spec, "anchor names that
@@ -2473,9 +2446,9 @@ static void Html_add_anchor(DilloHtml *html, const char *name)
*/
static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *old_style;
DilloUrl *url;
+ char *tooltip_str = NULL;
+ CssPropertyList props;
const char *attrbuf;
/* TODO: add support for MAP with A HREF */
@@ -2490,44 +2463,51 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
url = a_Html_url_new(html, attrbuf, NULL, 0);
dReturn_if_fail ( url != NULL );
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
-
- if (a_Capi_get_flags(url) & CAPI_IsCached) {
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_IsCached) {
html->InVisitedLink = true;
- style_attrs.color = Color::createSimple (
- HT2LT(html),
- html->visited_color
-/*
- a_Color_vc(html->visited_color,
- S_TOP(html)->style->color->getColor(),
- html->link_color,
- S_TOP(html)->current_bg_color),
-*/
- );
+ html->styleEngine->setPseudoVisited ();
+ if (html->non_css_visited_color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_visited_color);
} else {
- style_attrs.color = Color::createSimple(HT2LT(html),
- html->link_color);
+ html->styleEngine->setPseudoLink ();
+ if (html->non_css_link_color != -1)
+ props.set (CSS_PROPERTY_COLOR, CSS_TYPE_COLOR,
+ html->non_css_link_color);
}
-// if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "title")))
-// style_attrs.x_tooltip = a_Dw_tooltip_new_no_ref(attrbuf);
-
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- style_attrs.x_link = Html_set_new_link(html, &url);
- style_attrs.cursor = CURSOR_POINTER;
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ props.set (PROPERTY_X_LINK, CSS_TYPE_INTEGER,
+ Html_set_new_link(html, &url));
+ }
+ if (prefs.show_tooltip &&
+ (attrbuf = a_Html_get_attr(html, tag, tagsize, "title"))) {
+ tooltip_str = dStrdup(attrbuf);
+ props.set (PROPERTY_X_TOOLTIP, CSS_TYPE_STRING, tooltip_str);
}
+ html->styleEngine->setNonCssHints (&props);
+ dFree(tooltip_str);
+
+ html->styleEngine->inheritBackgroundColor ();
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "name"))) {
+ char *nameVal;
+ const char *id = html->styleEngine->getId ();
+
if (prefs.show_extra_warnings)
Html_check_name_val(html, attrbuf, "name");
- /* html->NameVal is freed in Html_process_tag */
- html->NameVal = a_Url_decode_hex_str(attrbuf);
- Html_add_anchor(html, html->NameVal);
+
+ nameVal = a_Url_decode_hex_str(attrbuf);
+
+ if (nameVal) {
+ /* We compare the "id" value with the url-decoded "name" value */
+ if (!id || strcmp(nameVal, id)) {
+ if (id)
+ BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
+ Html_add_anchor(html, nameVal);
+ }
+
+ dFree(nameVal);
+ }
}
}
@@ -2537,49 +2517,40 @@ static void Html_tag_open_a(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_a(DilloHtml *html, int TagIdx)
{
html->InVisitedLink = false;
- a_Html_pop_tag(html, TagIdx);
}
/*
- * Insert underlined text in the page.
+ * <BLOCKQUOTE>
*/
-static void Html_tag_open_u(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_blockquote(DilloHtml *html,
+ const char *tag, int tagsize)
{
- Style *style;
- StyleAttrs style_attrs;
-
- style = S_TOP(html)->style;
- style_attrs = *style;
- style_attrs.textDecoration |= TEXT_DECORATION_UNDERLINE;
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- style->unref ();
+ Html_add_textblock(html, 9);
}
/*
- * Insert strike-through text. Used by <S>, <STRIKE> and <DEL>.
+ * <Q>
*/
-static void Html_tag_open_strike(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_q(DilloHtml *html, const char *tag, int tagsize)
{
- Style *style;
- StyleAttrs style_attrs;
+ /*
+ * Left Double Quotation Mark, which is wrong in many cases, but
+ * should at least be widely recognized.
+ */
+ const char *U201C = "\xe2\x80\x9c";
- style = S_TOP(html)->style;
- style_attrs = *style;
- style_attrs.textDecoration |= TEXT_DECORATION_LINE_THROUGH;
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- style->unref ();
+ HT2TB(html)->addText (U201C, html->styleEngine->wordStyle ());
}
/*
- * <BLOCKQUOTE>
+ * </Q>
*/
-static void Html_tag_open_blockquote(DilloHtml *html,
- const char *tag, int tagsize)
+static void Html_tag_close_q(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 40, 9);
+ /* Right Double Quotation Mark */
+ const char *U201D = "\xe2\x80\x9d";
+
+ HT2TB(html)->addText (U201D, html->styleEngine->wordStyle ());
}
/*
@@ -2590,47 +2561,27 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
const char *attrbuf;
ListStyleType list_style_type;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
-
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ CssPropertyList props;
+
/* list_style_type explicitly defined */
- if (dStrncasecmp(attrbuf, "disc", 4) == 0)
+ if (dStrcasecmp(attrbuf, "disc") == 0)
list_style_type = LIST_STYLE_TYPE_DISC;
- else if (dStrncasecmp(attrbuf, "circle", 6) == 0)
+ else if (dStrcasecmp(attrbuf, "circle") == 0)
list_style_type = LIST_STYLE_TYPE_CIRCLE;
- else if (dStrncasecmp(attrbuf, "square", 6) == 0)
+ else if (dStrcasecmp(attrbuf, "square") == 0)
list_style_type = LIST_STYLE_TYPE_SQUARE;
else
/* invalid value */
list_style_type = LIST_STYLE_TYPE_DISC;
- } else {
- if (S_TOP(html)->list_type == HTML_LIST_UNORDERED) {
- /* Nested <UL>'s. */
- /* --EG :: I changed the behavior here : types are cycling instead of
- * being forced to square. It's easier for mixed lists level counting.
- */
- switch (S_TOP(html)->style->listStyleType) {
- case LIST_STYLE_TYPE_DISC:
- list_style_type = LIST_STYLE_TYPE_CIRCLE;
- break;
- case LIST_STYLE_TYPE_CIRCLE:
- list_style_type = LIST_STYLE_TYPE_SQUARE;
- break;
- case LIST_STYLE_TYPE_SQUARE:
- default: /* this is actually a bug */
- list_style_type = LIST_STYLE_TYPE_DISC;
- break;
- }
- } else {
- /* Either first <UL>, or a <OL> before. */
- list_style_type = LIST_STYLE_TYPE_DISC;
- }
+
+ props.set(CSS_PROPERTY_LIST_STYLE_TYPE, CSS_TYPE_ENUM, list_style_type);
+ html->styleEngine->setNonCssHints (&props);
}
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
- S_TOP(html)->list_type = HTML_LIST_UNORDERED;
+ Html_add_textblock(html, 9);
+ S_TOP(html)->list_type = HTML_LIST_UNORDERED;
S_TOP(html)->list_number = 0;
S_TOP(html)->ref_list_item = NULL;
}
@@ -2641,11 +2592,8 @@ static void Html_tag_open_ul(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dir(DilloHtml *html, const char *tag, int tagsize)
{
- ListStyleType list_style_type = LIST_STYLE_TYPE_DISC;
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
S_TOP(html)->list_type = HTML_LIST_UNORDERED;
S_TOP(html)->list_number = 0;
S_TOP(html)->ref_list_item = NULL;
@@ -2668,28 +2616,29 @@ static void Html_tag_open_menu(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- ListStyleType list_style_type;
int n = 1;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 0, 9);
-
- list_style_type = LIST_STYLE_TYPE_DECIMAL;
-
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "type"))) {
+ CssPropertyList props;
+ ListStyleType listStyleType = LIST_STYLE_TYPE_DECIMAL;
+
if (*attrbuf == '1')
- list_style_type = LIST_STYLE_TYPE_DECIMAL;
+ listStyleType = LIST_STYLE_TYPE_DECIMAL;
else if (*attrbuf == 'a')
- list_style_type = LIST_STYLE_TYPE_LOWER_ALPHA;
+ listStyleType = LIST_STYLE_TYPE_LOWER_ALPHA;
else if (*attrbuf == 'A')
- list_style_type = LIST_STYLE_TYPE_UPPER_ALPHA;
+ listStyleType = LIST_STYLE_TYPE_UPPER_ALPHA;
else if (*attrbuf == 'i')
- list_style_type = LIST_STYLE_TYPE_LOWER_ROMAN;
+ listStyleType = LIST_STYLE_TYPE_LOWER_ROMAN;
else if (*attrbuf == 'I')
- list_style_type = LIST_STYLE_TYPE_UPPER_ROMAN;
+ listStyleType = LIST_STYLE_TYPE_UPPER_ROMAN;
+
+ props.set (CSS_PROPERTY_LIST_STYLE_TYPE, CSS_TYPE_ENUM, listStyleType);
+ html->styleEngine->setNonCssHints (&props);
}
- HTML_SET_TOP_ATTR(html, listStyleType, list_style_type);
+ Html_add_textblock(html, 9);
+
S_TOP(html)->list_type = HTML_LIST_ORDERED;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "start")) &&
@@ -2706,59 +2655,45 @@ static void Html_tag_open_ol(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
{
- StyleAttrs style_attrs;
- Style *item_style, *word_style;
+ Style *style = html->styleEngine->style ();
+ Style *wordStyle = html->styleEngine->wordStyle ();
Widget **ref_list_item;
ListItem *list_item;
int *list_number;
const char *attrbuf;
char buf[16];
+ if (S_TOP(html)->list_type == HTML_LIST_NONE)
+ BUG_MSG("<li> outside <ul> or <ol>\n");
+
html->InFlags |= IN_LI;
- html->WordAfterLI = false;
/* Get our parent tag's variables (used as state storage) */
list_number = &html->stack->getRef(html->stack->size()-2)->list_number;
ref_list_item = &html->stack->getRef(html->stack->size()-2)->ref_list_item;
- /* set the item style */
- word_style = S_TOP(html)->style;
- style_attrs = *word_style;
- //style_attrs.backgroundColor = Color::createShaded (HT2LT(html), 0xffff40);
- //style_attrs.setBorderColor (Color::createSimple (HT2LT(html), 0x000000));
- //style_attrs.setBorderStyle (BORDER_SOLID);
- //style_attrs.borderWidth.setVal (1);
- item_style = Style::create (HT2LT(html), &style_attrs);
-
- DW2TB(html->dw)->addParbreak (2, word_style);
+ HT2TB(html)->addParbreak (0, wordStyle);
list_item = new ListItem ((ListItem*)*ref_list_item,prefs.limit_text_width);
- DW2TB(html->dw)->addWidget (list_item, item_style);
- DW2TB(html->dw)->addParbreak (2, word_style);
+ HT2TB(html)->addWidget (list_item, style);
+ HT2TB(html)->addParbreak (0, wordStyle);
*ref_list_item = list_item;
S_TOP(html)->textblock = html->dw = list_item;
- item_style->unref();
- /* Handle it when the user clicks on a link */
- html->connectSignals(list_item);
- switch (S_TOP(html)->list_type) {
- case HTML_LIST_ORDERED:
+ if (style->listStyleType == LIST_STYLE_TYPE_NONE) {
+ // none
+ } else if (style->listStyleType >= LIST_STYLE_TYPE_DECIMAL) {
+ // ordered
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "value")) &&
(*list_number = strtol(attrbuf, NULL, 10)) < 0) {
BUG_MSG("illegal negative LIST VALUE attribute; Starting from 0\n");
*list_number = 0;
}
- numtostr((*list_number)++, buf, 16, S_TOP(html)->style->listStyleType);
- list_item->initWithText (dStrdup(buf), word_style);
- list_item->addSpace (word_style);
- html->PrevWasSPC = true;
- break;
- case HTML_LIST_NONE:
- BUG_MSG("<li> outside <ul> or <ol>\n");
- default:
- list_item->initWithWidget (new Bullet(), word_style);
- list_item->addSpace (word_style);
- break;
+ numtostr((*list_number)++, buf, 16, style->listStyleType);
+ list_item->initWithText (buf, wordStyle);
+ } else {
+ // unordered
+ list_item->initWithWidget (new Bullet(), wordStyle);
}
}
@@ -2768,9 +2703,7 @@ static void Html_tag_open_li(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_li(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_LI;
- html->WordAfterLI = false;
((ListItem *)html->dw)->flush ();
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -2779,58 +2712,55 @@ static void Html_tag_close_li(DilloHtml *html, int TagIdx)
static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
{
Widget *hruler;
- StyleAttrs style_attrs;
- Style *style;
+ CssPropertyList props;
char *width_ptr;
const char *attrbuf;
int32_t size = 0;
-
- style_attrs = *S_TOP(html)->style;
- width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", "100%");
- style_attrs.width = a_Html_parse_length (html, width_ptr);
- dFree(width_ptr);
+ width_ptr = a_Html_get_attr_wdef(html, tag, tagsize, "width", NULL);
+ if (width_ptr) {
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, width_ptr));
+ dFree(width_ptr);
+ }
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "size")))
size = strtol(attrbuf, NULL, 10);
-
- if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
- if (dStrcasecmp (attrbuf, "left") == 0)
- style_attrs.textAlign = TEXT_ALIGN_LEFT;
- else if (dStrcasecmp (attrbuf, "right") == 0)
- style_attrs.textAlign = TEXT_ALIGN_RIGHT;
- else if (dStrcasecmp (attrbuf, "center") == 0)
- style_attrs.textAlign = TEXT_ALIGN_CENTER;
- }
-
+
+ a_Html_tag_set_align_attr(html, &props, tag, tagsize);
+
/* TODO: evaluate attribute */
if (a_Html_get_attr(html, tag, tagsize, "noshade")) {
- style_attrs.setBorderStyle (BORDER_SOLID);
- style_attrs.setBorderColor (
- Color::createShaded (HT2LT(html), style_attrs.color->getColor()));
- if (size < 1)
+ props.set (CSS_PROPERTY_BORDER_TOP_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_STYLE,CSS_TYPE_ENUM,BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_LEFT_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_STYLE, CSS_TYPE_ENUM, BORDER_SOLID);
+
+ if (size <= 0)
size = 1;
- } else {
- style_attrs.setBorderStyle (BORDER_INSET);
- style_attrs.setBorderColor
- (Color::createShaded (HT2LT(html),
- S_TOP(html)->current_bg_color));
- if (size < 2)
- size = 2;
- }
-
- style_attrs.borderWidth.top =
- style_attrs.borderWidth.left = (size + 1) / 2;
- style_attrs.borderWidth.bottom =
- style_attrs.borderWidth.right = size / 2;
- style = Style::create (HT2LT(html), &style_attrs);
-
- DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style);
+ }
+
+ if (size > 0) {
+ CssLength size_top = CSS_CREATE_LENGTH ((size+1)/2, CSS_LENGTH_TYPE_PX);
+ CssLength size_bottom = CSS_CREATE_LENGTH (size / 2, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_top);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_top);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_bottom);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ size_bottom);
+ }
+
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
+
hruler = new Ruler();
- hruler->setStyle (style);
- DW2TB(html->dw)->addWidget (hruler, style);
- style->unref ();
- DW2TB(html->dw)->addParbreak (5, S_TOP(html)->style);
+ hruler->setStyle (html->styleEngine->style ());
+ HT2TB(html)->addWidget (hruler, html->styleEngine->style ());
+ HT2TB(html)->addParbreak (5, html->styleEngine->wordStyle ());
}
/*
@@ -2839,7 +2769,7 @@ static void Html_tag_open_hr(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
{
/* may want to actually do some stuff here. */
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2847,8 +2777,7 @@ static void Html_tag_open_dl(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, NULL, 0, 1, 1);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2856,8 +2785,7 @@ static void Html_tag_open_dt(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- Html_add_indented(html, 40, 40, 9);
+ Html_add_textblock(html, 9);
}
/*
@@ -2865,14 +2793,8 @@ static void Html_tag_open_dd(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
- /* Is the placement of this statement right? */
- S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
- HTML_SET_TOP_ATTR (html, whiteSpace, WHITE_SPACE_PRE);
- html->pre_column = 0;
- html->PreFirstChar = true;
html->InFlags |= IN_PRE;
}
@@ -2882,8 +2804,7 @@ static void Html_tag_open_pre(DilloHtml *html, const char *tag, int tagsize)
static void Html_tag_close_pre(DilloHtml *html, int TagIdx)
{
html->InFlags &= ~IN_PRE;
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
/*
@@ -2899,7 +2820,7 @@ static int Html_tag_pre_excludes(int tag_idx)
/* initialize array */
if (!ei_set[0])
for (i = 0; es_set[i]; ++i)
- ei_set[i] = Html_tag_index(es_set[i]);
+ ei_set[i] = a_Html_tag_index(es_set[i]);
for (i = 0; ei_set[i]; ++i)
if (tag_idx == ei_set[i])
@@ -2909,12 +2830,12 @@ static int Html_tag_pre_excludes(int tag_idx)
/*
* Handle <META>
- * We do not support http-equiv=refresh because it's non standard,
- * (the HTML 4.01 SPEC recommends explicitly to avoid it), and it
- * can be easily abused!
- *
+ * We do not support http-equiv=refresh with delay>0 because it's
+ * non standard, (the HTML 4.01 SPEC recommends explicitly to avoid it).
* More info at:
* http://lists.w3.org/Archives/Public/www-html/2000Feb/thread.html#msg232
+ * Instant client-side redirects (delay=0) are supported:
+ * http://www.w3.org/TR/2008/NOTE-WCAG20-TECHS-20081211/H76.html
*
* TODO: Note that we're sending custom HTML while still IN_HEAD. This
* is a hackish way to put the message. A much cleaner approach is to
@@ -2930,9 +2851,8 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
" <tr><td bgcolor='#a0a0a0' colspan='2'>The author wanted you to go\n"
" <a href='%s'>here</a>%s</td></tr></table><br>\n";
- const char *equiv, *content;
- char delay_str[64];
- Dstr *ds_msg;
+ const char *p, *equiv, *content, *new_content;
+ char delay_str[64], *mr_url;
int delay;
/* only valid inside HEAD */
@@ -2943,49 +2863,178 @@ static void Html_tag_open_meta(DilloHtml *html, const char *tag, int tagsize)
if ((equiv = a_Html_get_attr(html, tag, tagsize, "http-equiv"))) {
if (!dStrcasecmp(equiv, "refresh") &&
- (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
+ (content = a_Html_get_attr(html, tag, tagsize, "content"))) {
/* Get delay, if present, and make a message with it */
- if ((delay = strtol(content, NULL, 0)))
+ if ((delay = strtol(content, NULL, 0))) {
snprintf(delay_str, 64, " after %d second%s.",
- delay, (delay > 1) ? "s" : "");
- else
+ delay, (delay > 1) ? "s" : "");
+ } else {
sprintf(delay_str, ".");
-
+ }
/* Skip to anything after "URL=" */
- while (*content && *(content++) != '=');
-
- /* Send a custom HTML message.
- * TODO: This is a hairy hack,
- * It'd be much better to build a widget. */
- ds_msg = dStr_sized_new(256);
- dStr_sprintf(ds_msg, meta_template, content, delay_str);
- {
- int SaveFlags = html->InFlags;
- html->InFlags = IN_BODY;
- html->TagSoup = false;
- Html_write_raw(html, ds_msg->str, ds_msg->len, 0);
- html->TagSoup = true;
- html->InFlags = SaveFlags;
+ while (*content && *(content++) != '=') ;
+ /* Handle the case of a quoted URL */
+ if (*content == '"' || *content == '\'') {
+ if ((p = strchr(content + 1, *content)))
+ mr_url = dStrndup(content + 1, p - content - 1);
+ else
+ mr_url = dStrdup(content + 1);
+ } else {
+ mr_url = dStrdup(content);
}
- dStr_free(ds_msg, 1);
+
+ if (delay == 0) {
+ /* zero-delay redirection */
+ html->stop_parser = true;
+ DilloUrl *new_url = a_Url_new(mr_url, URL_STR(html->base_url));
+ if (a_Capi_dpi_verify_request(html->bw, new_url))
+ a_UIcmd_redirection0((void*)html->bw, new_url);
+ a_Url_free(new_url);
+ } else {
+ /* Send a custom HTML message.
+ * TODO: This is a hairy hack,
+ * It'd be much better to build a widget. */
+ Dstr *ds_msg = dStr_sized_new(256);
+ dStr_sprintf(ds_msg, meta_template, mr_url, delay_str);
+ {
+ int o_InFlags = html->InFlags;
+ int o_TagSoup = html->TagSoup;
+ html->InFlags = IN_BODY;
+ html->TagSoup = false;
+ Html_write_raw(html, ds_msg->str, ds_msg->len, 0);
+ html->TagSoup = o_TagSoup;
+ html->InFlags = o_InFlags;
+ }
+ dStr_free(ds_msg, 1);
+ }
+ dFree(mr_url);
} else if (!dStrcasecmp(equiv, "content-type") &&
(content = a_Html_get_attr(html, tag, tagsize, "content"))) {
- if (a_Misc_content_type_cmp(html->content_type, content)) {
- const bool_t force = FALSE;
- const char *new_content =
- a_Capi_set_content_type(html->page_url, content, force);
- /* Cannot ask cache whether the content type was changed, as
- * this code in another bw might have already changed it for us.
- */
- if (a_Misc_content_type_cmp(html->content_type, new_content)) {
- a_Nav_repush(html->bw);
- html->stop_parser = true;
+ _MSG("Html_tag_open_meta: content={%s}\n", content);
+ /* Cannot ask cache whether the content type was changed, as
+ * this code in another bw might have already changed it for us.
+ */
+ new_content = a_Capi_set_content_type(html->page_url,content,"meta");
+ if (a_Misc_content_type_cmp(html->content_type, new_content)) {
+ html->stop_parser = true; /* The cache buffer is no longer valid */
+ a_UIcmd_repush(html->bw);
+ }
+ }
+ }
+}
+
+/*
+ * Called by the network engine when a stylesheet has new data.
+ */
+static void Html_css_load_callback(int Op, CacheClient_t *Client)
+{
+ _MSG("Html_css_load_callback: Op=%d\n", Op);
+ if (Op) { /* EOF */
+ BrowserWindow *bw = ((DilloWeb *)Client->Web)->bw;
+ /* Repush when we've got them all */
+ if (--bw->NumPendingStyleSheets == 0)
+ a_UIcmd_repush(bw);
+ }
+}
+
+/*
+ * Tell cache to retrieve a stylesheet
+ */
+void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url)
+{
+ char *data;
+ int len;
+
+ dReturn_if (url == NULL || ! prefs.load_stylesheets);
+
+ _MSG("Html_load_stylesheet: ");
+ if (a_Capi_get_buf(url, &data, &len)) {
+ _MSG("cached URL=%s len=%d", URL_STR(url), len);
+ if (a_Capi_get_flags_with_redirection(url) & CAPI_Completed) {
+ if (strncmp("@charset \"", data, 10) == 0) {
+ char *endq = strchr(data+10, '"');
+
+ if (endq && (endq - data <= 51)) {
+ /* IANA limits charset names to 40 characters */
+ const char *ignored;
+ char *content_type;
+
+ *endq = '\0';
+ content_type = dStrconcat("text/css; charset=", data+10, NULL);
+ *endq = '"';
+ a_Capi_unref_buf(url);
+ ignored = a_Capi_set_content_type(url, content_type, "meta");
+ dFree(content_type);
+ a_Capi_get_buf(url, &data, &len);
}
}
- }
+ html->styleEngine->parse(html, url, data, len, CSS_ORIGIN_AUTHOR);
+ }
+ a_Capi_unref_buf(url);
+ } else {
+ /* Fill a Web structure for the cache query */
+ int ClientKey;
+ DilloWeb *Web = a_Web_new(url, html->page_url);
+ Web->bw = html->bw;
+ if ((ClientKey = a_Capi_open_url(Web, Html_css_load_callback, NULL))) {
+ ++html->bw->NumPendingStyleSheets;
+ a_Bw_add_client(html->bw, ClientKey, 0);
+ a_Bw_add_url(html->bw, url);
+ MSG("NumPendingStyleSheets=%d", html->bw->NumPendingStyleSheets);
+ }
+ }
+ MSG("\n");
+}
+
+/*
+ * Parse the LINK element (Only CSS stylesheets by now).
+ * (If it either hits or misses, is not relevant here; that's up to the
+ * cache functions)
+ *
+ * TODO: How will we know when to use "handheld"? Ask the html->bw->ui for
+ * screen dimensions, or a dillorc preference.
+ */
+static void Html_tag_open_link(DilloHtml *html, const char *tag, int tagsize)
+{
+ DilloUrl *url;
+ const char *attrbuf;
+
+ //char *tag_str = dStrndup(tag, tagsize);
+ //MSG("Html_tag_open_link(): %s\n", tag_str);
+ //dFree(tag_str);
+
+ /* When viewing suspicious HTML email, don't load LINK */
+ dReturn_if (URL_FLAGS(html->base_url) & URL_SpamSafe);
+
+ /* Ignore LINK outside HEAD */
+ if (!(html->InFlags & IN_HEAD)) {
+ BUG_MSG("the LINK element must be inside the HEAD section\n");
+ return;
}
+ /* Remote stylesheets enabled? */
+ dReturn_if_fail (prefs.load_stylesheets);
+ /* CSS stylesheet link */
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "rel")) ||
+ dStrcasecmp(attrbuf, "stylesheet"))
+ return;
+
+ /* IMPLIED attributes? */
+ if (((attrbuf = a_Html_get_attr(html, tag, tagsize, "type")) &&
+ dStrcasecmp(attrbuf, "text/css")) ||
+ ((attrbuf = a_Html_get_attr(html, tag, tagsize, "media")) &&
+ !dStristr(attrbuf, "screen") && dStrcasecmp(attrbuf, "all")))
+ return;
+
+ if (!(attrbuf = a_Html_get_attr(html, tag, tagsize, "href")) ||
+ !(url = a_Html_url_new(html, attrbuf, NULL, 0)))
+ return;
+
+ MSG(" Html_tag_open_link(): addCssUrl %s\n", URL_STR(url));
+
+ html->addCssUrl(url);
+ a_Url_free(url);
}
/*
@@ -3031,60 +3080,9 @@ static void Html_tag_open_base(DilloHtml *html, const char *tag, int tagsize)
}
}
-/*
- * <CODE>
- */
-static void Html_tag_open_code(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <DFN>
- */
-static void Html_tag_open_dfn(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 3);
-}
-
-/*
- * <KBD>
- */
-static void Html_tag_open_kbd(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <SAMP>
- */
-static void Html_tag_open_samp(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, prefs.fw_fontname, 0, 0, 0);
-}
-
-/*
- * <VAR>
- */
-static void Html_tag_open_var(DilloHtml *html, const char *tag, int tagsize)
-{
- a_Html_set_top_font(html, NULL, 0, 2, 2);
-}
-
-/*
- * <SUB>
- */
-static void Html_tag_open_sub(DilloHtml *html, const char *tag, int tagsize)
-{
- HTML_SET_TOP_ATTR (html, valign, VALIGN_SUB);
-}
-
-/*
- * <SUP>
- */
-static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize)
+static void Html_tag_open_default(DilloHtml *html,const char *tag,int tagsize)
{
- HTML_SET_TOP_ATTR (html, valign, VALIGN_SUPER);
+ html->styleEngine->inheritBackgroundColor();
}
/*
@@ -3092,25 +3090,18 @@ static void Html_tag_open_sup(DilloHtml *html, const char *tag, int tagsize)
*/
static void Html_tag_open_div(DilloHtml *html, const char *tag, int tagsize)
{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- a_Html_tag_set_align_attr (html, tag, tagsize);
-}
+ CssPropertyList props;
-/*
- * </DIV>, also used for </TABLE> and </CENTER>
- */
-static void Html_tag_close_div(DilloHtml *html, int TagIdx)
-{
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
+ html->styleEngine->setNonCssHints (&props);
+ Html_add_textblock(html, 0);
}
/*
- * Default close for most tags - just pop the stack.
+ * Default close for most tags.
*/
static void Html_tag_close_default(DilloHtml *html, int TagIdx)
{
- a_Html_pop_tag(html, TagIdx);
}
/*
@@ -3118,8 +3109,7 @@ static void Html_tag_close_default(DilloHtml *html, int TagIdx)
*/
static void Html_tag_close_par(DilloHtml *html, int TagIdx)
{
- DW2TB(html->dw)->addParbreak (9, S_TOP(html)->style);
- a_Html_pop_tag(html, TagIdx);
+ HT2TB(html)->addParbreak (9, html->styleEngine->wordStyle ());
}
@@ -3158,55 +3148,56 @@ const TagInfo Tags[] = {
/* acronym 010101 */
{"address", B8(010110),'R',2, Html_tag_open_address, Html_tag_close_par},
{"area", B8(010001),'F',0, Html_tag_open_area, Html_tag_close_default},
- {"b", B8(010101),'R',2, Html_tag_open_b, Html_tag_close_default},
+ {"b", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"base", B8(100001),'F',0, Html_tag_open_base, Html_tag_close_default},
/* basefont 010001 */
/* bdo 010101 */
- {"big", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
- {"blockquote", B8(011110),'R',2,Html_tag_open_blockquote,Html_tag_close_par},
+ {"big", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"blockquote", B8(011110),'R',2, Html_tag_open_blockquote,
+ Html_tag_close_default},
{"body", B8(011110),'O',1, Html_tag_open_body, Html_tag_close_body},
{"br", B8(010001),'F',0, Html_tag_open_br, Html_tag_close_default},
{"button", B8(011101),'R',2, Html_tag_open_button, Html_tag_close_button},
/* caption */
- {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_div},
- {"cite", B8(010101),'R',2, Html_tag_open_cite, Html_tag_close_default},
- {"code", B8(010101),'R',2, Html_tag_open_code, Html_tag_close_default},
+ {"center", B8(011110),'R',2, Html_tag_open_center, Html_tag_close_center},
+ {"cite", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"code", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* col 010010 'F' */
/* colgroup */
- {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_par},
- {"del", B8(011101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"dfn", B8(010101),'R',2, Html_tag_open_dfn, Html_tag_close_default},
+ {"dd", B8(011110),'O',1, Html_tag_open_dd, Html_tag_close_default},
+ {"del", B8(011101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"dfn", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"dir", B8(011010),'R',2, Html_tag_open_dir, Html_tag_close_par},
/* TODO: complete <div> support! */
- {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_div},
+ {"div", B8(011110),'R',2, Html_tag_open_div, Html_tag_close_default},
{"dl", B8(011010),'R',2, Html_tag_open_dl, Html_tag_close_par},
{"dt", B8(010110),'O',1, Html_tag_open_dt, Html_tag_close_par},
- {"em", B8(010101),'R',2, Html_tag_open_em, Html_tag_close_default},
+ {"em", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* fieldset */
{"font", B8(010101),'R',2, Html_tag_open_font, Html_tag_close_default},
{"form", B8(011110),'R',2, Html_tag_open_form, Html_tag_close_form},
{"frame", B8(010010),'F',0, Html_tag_open_frame, Html_tag_close_default},
{"frameset", B8(011110),'R',2,Html_tag_open_frameset, Html_tag_close_default},
- {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
- {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_h},
+ {"h1", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h2", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h3", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h4", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h5", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
+ {"h6", B8(010110),'R',2, Html_tag_open_h, Html_tag_close_par},
{"head", B8(101101),'O',1, Html_tag_open_head, Html_tag_close_head},
{"hr", B8(010010),'F',0, Html_tag_open_hr, Html_tag_close_default},
{"html", B8(001110),'O',1, Html_tag_open_html, Html_tag_close_html},
- {"i", B8(010101),'R',2, Html_tag_open_i, Html_tag_close_default},
+ {"i", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"iframe", B8(011110),'R',2, Html_tag_open_frame, Html_tag_close_default},
{"img", B8(010001),'F',0, Html_tag_open_img, Html_tag_close_default},
{"input", B8(010001),'F',0, Html_tag_open_input, Html_tag_close_default},
/* ins */
{"isindex", B8(110001),'F',0, Html_tag_open_isindex, Html_tag_close_default},
- {"kbd", B8(010101),'R',2, Html_tag_open_kbd, Html_tag_close_default},
+ {"kbd", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
/* label 010101 */
/* legend 01?? */
{"li", B8(011110),'O',1, Html_tag_open_li, Html_tag_close_li},
- /* link 100000 'F' */
+ {"link", B8(100001),'F',0, Html_tag_open_link, Html_tag_close_default},
{"map", B8(011001),'R',2, Html_tag_open_map, Html_tag_close_map},
/* menu 1010 -- TODO: not exactly 1010, it can contain LI and inline */
{"menu", B8(011010),'R',2, Html_tag_open_menu, Html_tag_close_par},
@@ -3214,25 +3205,25 @@ const TagInfo Tags[] = {
/* noframes 1011 */
/* noscript 1011 */
{"object", B8(111101),'R',2, Html_tag_open_object, Html_tag_close_default},
- {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_par},
+ {"ol", B8(011010),'R',2, Html_tag_open_ol, Html_tag_close_default},
/* optgroup */
{"option", B8(010001),'O',1, Html_tag_open_option, Html_tag_close_default},
{"p", B8(010110),'O',1, Html_tag_open_p, Html_tag_close_par},
/* param 010001 'F' */
{"pre", B8(010110),'R',2, Html_tag_open_pre, Html_tag_close_pre},
- /* q 010101 */
- {"s", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"samp", B8(010101),'R',2, Html_tag_open_samp, Html_tag_close_default},
+ {"q", B8(010101),'R',2, Html_tag_open_q, Html_tag_close_q},
+ {"s", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"samp", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"script", B8(111001),'R',2, Html_tag_open_script, Html_tag_close_script},
{"select", B8(010101),'R',2, Html_tag_open_select, Html_tag_close_select},
- {"small", B8(010101),'R',2, Html_tag_open_big_small, Html_tag_close_default},
- /* span 0101 */
- {"strike", B8(010101),'R',2, Html_tag_open_strike, Html_tag_close_default},
- {"strong", B8(010101),'R',2, Html_tag_open_strong, Html_tag_close_default},
+ {"small", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"span", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"strike", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"strong", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
{"style", B8(100101),'R',2, Html_tag_open_style, Html_tag_close_style},
- {"sub", B8(010101),'R',2, Html_tag_open_sub, Html_tag_close_default},
- {"sup", B8(010101),'R',2, Html_tag_open_sup, Html_tag_close_default},
- {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_div},
+ {"sub", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"sup", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"table", B8(011010),'R',5, Html_tag_open_table, Html_tag_close_center},
/* tbody */
{"td", B8(011110),'O',3, Html_tag_open_td, Html_tag_close_default},
{"textarea", B8(010101),'R',2,Html_tag_open_textarea,Html_tag_close_textarea},
@@ -3241,10 +3232,10 @@ const TagInfo Tags[] = {
/* thead */
{"title", B8(100101),'R',2, Html_tag_open_title, Html_tag_close_title},
{"tr", B8(011010),'O',4, Html_tag_open_tr, Html_tag_close_default},
- {"tt", B8(010101),'R',2, Html_tag_open_tt, Html_tag_close_default},
- {"u", B8(010101),'R',2, Html_tag_open_u, Html_tag_close_default},
- {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_par},
- {"var", B8(010101),'R',2, Html_tag_open_var, Html_tag_close_default}
+ {"tt", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"u", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default},
+ {"ul", B8(011010),'R',2, Html_tag_open_ul, Html_tag_close_default},
+ {"var", B8(010101),'R',2, Html_tag_open_default, Html_tag_close_default}
};
#define NTAGS (sizeof(Tags)/sizeof(Tags[0]))
@@ -3270,7 +3261,7 @@ static int Html_tag_compare(const char *p1, const char *p2)
* Get 'tag' index
* return -1 if tag is not handled yet
*/
-static int Html_tag_index(const char *tag)
+int a_Html_tag_index(const char *tag)
{
int low, high, mid, cond;
@@ -3301,17 +3292,17 @@ static int Html_needs_optional_close(int old_idx, int cur_idx)
if (i_P == -1) {
/* initialize the indexes of elements with optional close */
- i_P = Html_tag_index("p"),
- i_LI = Html_tag_index("li"),
- i_TD = Html_tag_index("td"),
- i_TR = Html_tag_index("tr"),
- i_TH = Html_tag_index("th"),
- i_DD = Html_tag_index("dd"),
- i_DT = Html_tag_index("dt"),
- i_OPTION = Html_tag_index("option");
- // i_THEAD = Html_tag_index("thead");
- // i_TFOOT = Html_tag_index("tfoot");
- // i_COLGROUP = Html_tag_index("colgroup");
+ i_P = a_Html_tag_index("p"),
+ i_LI = a_Html_tag_index("li"),
+ i_TD = a_Html_tag_index("td"),
+ i_TR = a_Html_tag_index("tr"),
+ i_TH = a_Html_tag_index("th"),
+ i_DD = a_Html_tag_index("dd"),
+ i_DT = a_Html_tag_index("dt"),
+ i_OPTION = a_Html_tag_index("option");
+ // i_THEAD = a_Html_tag_index("thead");
+ // i_TFOOT = a_Html_tag_index("tfoot");
+ // i_COLGROUP = a_Html_tag_index("colgroup");
}
if (old_idx == i_P || old_idx == i_DT) {
@@ -3408,10 +3399,11 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
if (!(html->InFlags & IN_HTML)) {
tag = "<html>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
@@ -3420,10 +3412,11 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
/* head element */
if (!(html->InFlags & IN_HEAD)) {
tag = "<head>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open of the head element */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
@@ -3432,20 +3425,57 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
/* body element */
if (html->InFlags & IN_HEAD) {
tag = "</head>";
- tag_idx = Html_tag_index(tag + 2);
- Tags[tag_idx].close (html, tag_idx);
+ tag_idx = a_Html_tag_index(tag + 2);
+ Html_tag_cleanup_at_close(html, tag_idx);
}
tag = "<body>";
- tag_idx = Html_tag_index(tag + 1);
+ tag_idx = a_Html_tag_index(tag + 1);
if (tag_idx != new_idx || IsCloseTag) {
/* implicit open */
Html_force_push_tag(html, tag_idx);
+ _MSG("Open : %*s%s\n", html->stack->size()," ",Tags[tag_idx].name);
Tags[tag_idx].open (html, tag, strlen(tag));
}
}
}
/*
+ * Parse attributes that can appear on any tag.
+ */
+static void Html_parse_common_attrs(DilloHtml *html, char *tag, int tagsize)
+{
+ const char *attrbuf;
+
+ if (tagsize >= 8 && /* length of "<t id=i>" */
+ (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
+ HTML_LeftTrim | HTML_RightTrim))) {
+ /* According to the SGML declaration of HTML 4, all NAME values
+ * occuring outside entities must be converted to uppercase
+ * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
+ * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
+ * So we don't do it and hope for better specs in the future ...
+ */
+ Html_check_name_val(html, attrbuf, "id");
+
+ html->styleEngine->setId(attrbuf);
+ }
+
+ if (tagsize >= 11 && (prefs.parse_embedded_css || prefs.load_stylesheets)) {
+ /* length of "<t class=i>" or "<t style=i>" */
+ attrbuf = Html_get_attr2(html, tag, tagsize, "class",
+ HTML_LeftTrim | HTML_RightTrim);
+ if (attrbuf)
+ html->styleEngine->setClass (attrbuf);
+
+ attrbuf = Html_get_attr2(html, tag, tagsize, "style",
+ HTML_LeftTrim | HTML_RightTrim);
+ if (attrbuf)
+ html->styleEngine->setStyle (attrbuf);
+ }
+
+}
+
+/*
* Process a tag, given as 'tag' and 'tagsize'. -- tagsize is [1 based]
* ('tag' must include the enclosing angle brackets)
* This function calls the right open or close function for the tag.
@@ -3453,11 +3483,12 @@ static void Html_test_section(DilloHtml *html, int new_idx, int IsCloseTag)
static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
{
int ci, ni; /* current and new tag indexes */
- const char *attrbuf;
char *start = tag + 1; /* discard the '<' */
int IsCloseTag = (*start == '/');
- ni = Html_tag_index(start + IsCloseTag);
+ dReturn_if (html->stop_parser == true);
+
+ ni = a_Html_tag_index(start + IsCloseTag);
if (ni == -1) {
/* TODO: doctype parsing is a bit fuzzy, but enough for the time being */
if (!(html->InFlags & IN_HTML)) {
@@ -3491,40 +3522,30 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* Push the tag into the stack */
Html_push_tag(html, ni);
+ html->styleEngine->startElement (ni);
+ _MSG("Open : %*s%s\n", html->stack->size(), " ", Tags[ni].name);
+
+ /* Parse attributes that can appear on any tag */
+ Html_parse_common_attrs(html, tag, tagsize);
+
/* Call the open function for this tag */
+ _MSG("Open : %s\n", Tags[ni].name);
Tags[ni].open (html, tag, tagsize);
if (html->stop_parser)
break;
- /* Now parse attributes that can appear on any tag */
- if (tagsize >= 8 && /* length of "<t id=i>" */
- (attrbuf = Html_get_attr2(html, tag, tagsize, "id",
- HTML_LeftTrim | HTML_RightTrim))) {
- /* According to the SGML declaration of HTML 4, all NAME values
- * occuring outside entities must be converted to uppercase
- * (this is what "NAMECASE GENERAL YES" says). But the HTML 4
- * spec states in Sec. 7.5.2 that anchor ids are case-sensitive.
- * So we don't do it and hope for better specs in the future ...
- */
- Html_check_name_val(html, attrbuf, "id");
- /* We compare the "id" value with the url-decoded "name" value */
- if (!html->NameVal || strcmp(html->NameVal, attrbuf)) {
- if (html->NameVal)
- BUG_MSG("'id' and 'name' attribute of <a> tag differ\n");
- Html_add_anchor(html, attrbuf);
- }
+ if (S_TOP(html)->parse_mode != DILLO_HTML_PARSE_MODE_PRE &&
+ (html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE ||
+ html->styleEngine->style ()->whiteSpace == WHITE_SPACE_PRE_WRAP)) {
+ S_TOP(html)->parse_mode = DILLO_HTML_PARSE_MODE_PRE;
+ html->pre_column = 0;
+ html->PreFirstChar = true;
}
- /* Reset NameVal */
- if (html->NameVal) {
- dFree(html->NameVal);
- html->NameVal = NULL;
- }
-
- /* let the parser know this was an open tag */
- html->PrevWasOpenTag = true;
+ if (html->styleEngine->getId ())
+ Html_add_anchor(html, html->styleEngine->getId ());
- /* Request inmediate close for elements with forbidden close tag. */
+ /* Request immediate close for elements with forbidden close tag. */
/* TODO: XHTML always requires close tags. A simple implementation
* of the commented clause below will make it work. */
if (/* parsing HTML && */ Tags[ni].EndTag == 'F')
@@ -3538,13 +3559,13 @@ static void Html_process_tag(DilloHtml *html, char *tag, int tagsize)
/* Test for </x>, ReqTagClose, <x /> and <x/> */
if (*start == '/' || /* </x> */
html->ReqTagClose || /* request */
- (tag[tagsize - 2] == '/' && /* XML: */
- (isspace(tag[tagsize - 3]) || /* <x /> */
+ (tag[tagsize-2] == '/' && /* XML: */
+ (strchr(" \"'", tag[tagsize-3]) || /* [ "']/> */
(size_t)tagsize == strlen(Tags[ni].name) + 3))) { /* <x/> */
-
- Tags[ni].close (html, ni);
+
+ _MSG("Close: %s\n", Tags[ni].name);
+ Html_tag_cleanup_at_close(html, ni);
/* This was a close tag */
- html->PrevWasOpenTag = false;
html->ReqTagClose = false;
}
}
@@ -3623,7 +3644,7 @@ static const char *Html_get_attr2(DilloHtml *html,
tagsize-i, &entsize)) >= 0) {
if (isocode >= 128) {
char buf[4];
- int k, n = utf8encode(isocode, buf);
+ int k, n = a_Utf8_encode(isocode, buf);
for (k = 0; k < n; ++k)
dStr_append_c(Buf, buf[k]);
} else {
@@ -3687,29 +3708,6 @@ char *a_Html_get_attr_wdef(DilloHtml *html,
}
/*
- * Add a widget to the page.
- */
-static void Html_add_widget(DilloHtml *html,
- Widget *widget,
- char *width_str,
- char *height_str,
- StyleAttrs *style_attrs)
-{
- StyleAttrs new_style_attrs;
- Style *style;
-
- new_style_attrs = *style_attrs;
- new_style_attrs.width = width_str ?
- a_Html_parse_length (html, width_str) : LENGTH_AUTO;
- new_style_attrs.height = height_str ?
- a_Html_parse_length (html, height_str) : LENGTH_AUTO;
- style = Style::create (HT2LT(html), &new_style_attrs);
- DW2TB(html->dw)->addWidget (widget, style);
- style->unref ();
-}
-
-
-/*
* Dispatch the apropriate function for 'Op'
* This function is a Cache client and gets called whenever new data arrives
* Op : operation to perform.
@@ -3739,13 +3737,13 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
Textblock *textblock;
int token_start, buf_index;
- dReturn_val_if_fail ((textblock = DW2TB(html->dw)) != NULL, 0);
+ dReturn_val_if_fail ((textblock = HT2TB(html)) != NULL, 0);
/* Now, 'buf' and 'bufsize' define a buffer aligned to start at a token
* boundary. Iterate through tokens until end of buffer is reached. */
buf_index = 0;
token_start = buf_index;
- while ((buf_index < bufsize) && (html->stop_parser == false)) {
+ while ((buf_index < bufsize) && !html->stop_parser) {
/* invariant: buf_index == bufsize || token_start == buf_index */
if (S_TOP(html)->parse_mode ==
@@ -3774,7 +3772,7 @@ static int Html_write_raw(DilloHtml *html, char *buf, int bufsize, int Eof)
if (isspace(buf[buf_index])) {
/* whitespace: group all available whitespace */
- while (++buf_index < bufsize && isspace(buf[buf_index]));
+ while (++buf_index < bufsize && isspace(buf[buf_index])) ;
Html_process_space(html, buf + token_start, buf_index - token_start);
token_start = buf_index;
diff --git a/src/html.hh b/src/html.hh
index 3e08f37a..5b18c1a8 100644
--- a/src/html.hh
+++ b/src/html.hh
@@ -11,6 +11,9 @@ extern "C" {
* Exported functions
*/
void a_Html_load_images(void *v_html, DilloUrl *pattern);
+void a_Html_form_submit(void *v_html, void *v_form);
+void a_Html_form_reset(void *v_html, void *v_form);
+void a_Html_form_display_hiddens(void *v_html, void *v_form, bool_t display);
#ifdef __cplusplus
}
diff --git a/src/html_common.hh b/src/html_common.hh
index c694123e..3cca82de 100644
--- a/src/html_common.hh
+++ b/src/html_common.hh
@@ -13,12 +13,14 @@
#include "form.hh"
+#include "styleengine.hh"
+
/*
- * Macros
+ * Macros
*/
-// Dw to Textblock
-#define DW2TB(dw) ((Textblock*)dw)
+// "html struct" to Textblock
+#define HT2TB(html) ((Textblock*)(html->dw))
// "html struct" to "Layout"
#define HT2LT(html) ((Layout*)html->bw->render_layout)
// "Image" to "Dw Widget"
@@ -33,32 +35,15 @@
} D_STMT_END
/*
- * Change one toplevel attribute. var should be an identifier. val is
- * only evaluated once, so you can safely use a function call for it.
+ * Typedefs
*/
-#define HTML_SET_TOP_ATTR(html, var, val) \
- do { \
- StyleAttrs style_attrs; \
- Style *old_style; \
- \
- old_style = S_TOP(html)->style; \
- style_attrs = *old_style; \
- style_attrs.var = (val); \
- S_TOP(html)->style = \
- Style::create (HT2LT(html), &style_attrs); \
- old_style->unref (); \
- } while (FALSE)
-/*
- * Typedefs
- */
-
-typedef struct _DilloLinkImage DilloLinkImage;
+typedef struct _DilloHtmlImage DilloHtmlImage;
typedef struct _DilloHtmlState DilloHtmlState;
typedef enum {
- DT_NONE,
- DT_HTML,
+ DT_NONE,
+ DT_HTML,
DT_XHTML
} DilloHtmlDocumentType;
@@ -100,16 +85,16 @@ typedef enum {
} DilloHtmlProcessingState;
/*
- * Data Structures
+ * Data Structures
*/
-struct _DilloLinkImage {
+struct _DilloHtmlImage {
DilloUrl *url;
DilloImage *image;
};
struct _DilloHtmlState {
- dw::core::style::Style *style, *table_cell_style;
+ CssPropertyList *table_cell_props;
DilloHtmlParseMode parse_mode;
DilloHtmlTableMode table_mode;
bool cell_text_align_set;
@@ -124,10 +109,6 @@ struct _DilloHtmlState {
/* This is used to align list items (especially in enumerated lists) */
dw::core::Widget *ref_list_item;
- /* This makes image processing faster than a function
- a_Dw_widget_get_background_color. */
- int32_t current_bg_color;
-
/* This is used for list items etc; if it is set to TRUE, breaks
have to be "handed over" (see Html_add_indented and
Html_eventually_pop_dw). */
@@ -135,12 +116,12 @@ struct _DilloHtmlState {
};
/*
- * Classes
+ * Classes
*/
class DilloHtml {
private:
- class HtmlLinkReceiver: public dw::core::Widget::LinkReceiver {
+ class HtmlLinkReceiver: public dw::core::Layout::LinkReceiver {
public:
DilloHtml *html;
@@ -161,7 +142,6 @@ public: //BUG: for now everything is public
/* -------------------------------------------------------------------*/
/* Variables required at parsing time */
/* -------------------------------------------------------------------*/
- size_t Buf_Consumed; /* amount of source from cache consumed */
char *Start_Buf;
int Start_Ofs;
char *content_type, *charset;
@@ -173,7 +153,11 @@ public: //BUG: for now everything is public
DilloHtmlDocumentType DocType; /* as given by DOCTYPE tag */
float DocTypeVersion; /* HTML or XHTML version number */
+ /* vector of remote CSS resources, as given by the LINK element */
+ lout::misc::SimpleVector<DilloUrl*> *cssUrls;
+
lout::misc::SimpleVector<DilloHtmlState> *stack;
+ StyleEngine *styleEngine;
int InFlags; /* tracks which elements we are in */
@@ -184,33 +168,30 @@ public: //BUG: for now everything is public
bool PreFirstChar; /* used to skip the first CR or CRLF in PRE tags */
bool PrevWasCR; /* Flag to help parsing of "\r\n" in PRE tags */
bool PrevWasOpenTag; /* Flag to help deferred parsing of white space */
- bool PrevWasSPC; /* Flag to help handling collapsing white space */
bool InVisitedLink; /* used to 'contrast_visited_colors' */
bool ReqTagClose; /* Flag to help handling bad-formed HTML */
- bool CloseOneTag; /* Flag to help Html_tag_cleanup_at_close() */
- bool WordAfterLI; /* Flag to help ignoring the 1st <P> after <LI> */
bool TagSoup; /* Flag to enable the parser's cleanup functions */
- char *NameVal; /* used for validation of "NAME" and "ID" in <A> */
+ bool loadCssFromStash; /* current stash content should be loaded as CSS */
/* element counters: used for validation purposes */
uchar_t Num_HTML, Num_HEAD, Num_BODY, Num_TITLE;
Dstr *attr_data; /* Buffer for attribute value */
+ int32_t non_css_link_color; /* as provided by link attribute in BODY */
+ int32_t non_css_visited_color; /* as provided by vlink attribute in BODY */
+ int32_t visited_color; /* as computed according to CSS */
+
/* -------------------------------------------------------------------*/
/* Variables required after parsing (for page functionality) */
/* -------------------------------------------------------------------*/
lout::misc::SimpleVector<DilloHtmlForm*> *forms;
lout::misc::SimpleVector<DilloHtmlInput*> *inputs_outside_form;
lout::misc::SimpleVector<DilloUrl*> *links;
- lout::misc::SimpleVector<DilloLinkImage*> *images;
+ lout::misc::SimpleVector<DilloHtmlImage*> *images;
dw::ImageMapsList maps;
- int32_t link_color;
- int32_t visited_color;
-
private:
- bool parse_finished;
void freeParseData();
void initDw(); /* Used by the constructor */
@@ -227,12 +208,15 @@ public:
DilloHtmlForm *getCurrentForm ();
bool_t unloadedImages();
void loadImages (const DilloUrl *pattern);
+ void addCssUrl(const DilloUrl *url);
};
/*
- * Parser functions
+ * Parser functions
*/
+int a_Html_tag_index(const char *tag);
+
const char *a_Html_get_attr(DilloHtml *html,
const char *tag,
int tagsize,
@@ -248,10 +232,8 @@ DilloUrl *a_Html_url_new(DilloHtml *html,
const char *url_str, const char *base_url,
int use_base_url);
-DilloImage *a_Html_add_new_image(DilloHtml *html, const char *tag,
- int tagsize, DilloUrl *url,
- dw::core::style::StyleAttrs *style_attrs,
- bool add);
+DilloImage *a_Html_image_new(DilloHtml *html, const char *tag,
+ int tagsize, DilloUrl *url);
char *a_Html_parse_entities(DilloHtml *html, const char *token, int toksize);
void a_Html_pop_tag(DilloHtml *html, int TagIdx);
@@ -260,12 +242,12 @@ int32_t a_Html_color_parse(DilloHtml *html,
const char *subtag, int32_t default_color);
dw::core::style::Length a_Html_parse_length (DilloHtml *html,
const char *attr);
-void a_Html_tag_set_align_attr(DilloHtml *html,
+void a_Html_tag_set_align_attr(DilloHtml *html, CssPropertyList *props,
const char *tag, int tagsize);
bool a_Html_tag_set_valign_attr(DilloHtml *html,
const char *tag, int tagsize,
- dw::core::style::StyleAttrs *style_attrs);
-void a_Html_set_top_font(DilloHtml *html, const char *name, int size,
- int BI, int BImask);
+ CssPropertyList *props);
+
+void a_Html_load_stylesheet(DilloHtml *html, DilloUrl *url);
#endif /* __HTML_COMMON_HH__ */
diff --git a/src/image.cc b/src/image.cc
index 54eb4710..c499d977 100644
--- a/src/image.cc
+++ b/src/image.cc
@@ -15,9 +15,6 @@
* of data from an Image to a DwImage widget.
*/
-#include <stdio.h>
-#include <string.h>
-
#include "msg.h"
#include "image.hh"
@@ -27,23 +24,13 @@
using namespace dw::core;
// Image to Object-Image macro
-#define OI(Image) ((dw::Image*)(Image->dw))
-
-
-/*
- * Local data
- */
-static size_t linebuf_size = 0;
-static uchar_t *linebuf = NULL;
+#define I2DW(Image) ((dw::Image*)(Image->dw))
/*
* Create and initialize a new image structure.
*/
-DilloImage *a_Image_new(int width,
- int height,
- const char *alt_text,
- int32_t bg_color)
+DilloImage *a_Image_new(const char *alt_text, int32_t bg_color)
{
DilloImage *Image;
@@ -51,15 +38,12 @@ DilloImage *a_Image_new(int width,
Image->dw = (void*) new dw::Image(alt_text);
Image->width = 0;
Image->height = 0;
- Image->cmap = NULL;
- Image->in_type = DILLO_IMG_TYPE_NOTSET;
Image->bg_color = bg_color;
- Image->ProcessedBytes = 0;
Image->ScanNumber = 0;
Image->BitVec = NULL;
Image->State = IMG_Empty;
- Image->RefCount = 1;
+ Image->RefCount = 0;
return Image;
}
@@ -75,6 +59,7 @@ static void Image_free(DilloImage *Image)
/*
* Unref and free if necessary
+ * Do nothing if the argument is NULL
*/
void a_Image_unref(DilloImage *Image)
{
@@ -85,6 +70,7 @@ void a_Image_unref(DilloImage *Image)
/*
* Add a reference to an Image struct
+ * Do nothing if the argument is NULL
*/
void a_Image_ref(DilloImage *Image)
{
@@ -93,37 +79,6 @@ void a_Image_ref(DilloImage *Image)
}
/*
- * Decode 'buf' (an image line) into RGB format.
- */
-static uchar_t *
- Image_line(DilloImage *Image, const uchar_t *buf, const uchar_t *cmap, int y)
-{
- uint_t x;
-
- switch (Image->in_type) {
- case DILLO_IMG_TYPE_INDEXED:
- if (cmap) {
- for (x = 0; x < Image->width; x++)
- memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
- } else {
- MSG("Gif:: WARNING, image lacks a color map\n");
- }
- break;
- case DILLO_IMG_TYPE_GRAY:
- for (x = 0; x < Image->width; x++)
- memset(linebuf + x * 3, buf[x], 3);
- break;
- case DILLO_IMG_TYPE_RGB:
- /* avoid a memcpy here! --Jcid */
- return (uchar_t *)buf;
- case DILLO_IMG_TYPE_NOTSET:
- MSG_ERR("Image_line: type not set...\n");
- break;
- }
- return linebuf;
-}
-
-/*
* Set initial parameters of the image
*/
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
@@ -133,59 +88,27 @@ void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
_MSG("a_Image_set_parms: width=%d height=%d\n", width, height);
bool resize = (Image->width != width || Image->height != height);
- OI(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
+ I2DW(Image)->setBuffer((Imgbuf*)v_imgbuf, resize);
if (!Image->BitVec)
Image->BitVec = a_Bitvec_new(height);
- Image->in_type = type;
Image->width = width;
Image->height = height;
- if (3 * width > linebuf_size) {
- linebuf_size = 3 * width;
- linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
- }
Image->State = IMG_SetParms;
}
/*
- * Reference the dicache entry color map
- */
-void a_Image_set_cmap(DilloImage *Image, const uchar_t *cmap)
-{
- Image->cmap = cmap;
- Image->State = IMG_SetCmap;
-}
-
-/*
- * Begin a new scan for a multiple-scan image
- */
-void a_Image_new_scan(DilloImage *Image, void *v_imgbuf)
-{
- a_Bitvec_clear(Image->BitVec);
- Image->ScanNumber++;
- ((Imgbuf*)v_imgbuf)->newScan();
-}
-
-/*
* Implement the write method
*/
-void a_Image_write(DilloImage *Image, void *v_imgbuf,
- const uchar_t *buf, uint_t y, int decode)
+void a_Image_write(DilloImage *Image, uint_t y)
{
- uchar_t *newbuf;
-
+ _MSG("a_Image_write\n");
dReturn_if_fail ( y < Image->height );
- if (decode) {
- /* Decode 'buf' and copy it into the DicEntry buffer */
- newbuf = Image_line(Image, buf, Image->cmap, y);
- ((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
- }
+ /* Update the row in DwImage */
+ I2DW(Image)->drawRow(y);
a_Bitvec_set_bit(Image->BitVec, y);
Image->State = IMG_Write;
-
- /* Update the row in DwImage */
- OI(Image)->drawRow(y);
}
/*
@@ -193,46 +116,6 @@ void a_Image_write(DilloImage *Image, void *v_imgbuf,
*/
void a_Image_close(DilloImage *Image)
{
- a_Image_unref(Image);
-}
-
-
-// Wrappers for Imgbuf -------------------------------------------------------
-
-/*
- * Increment reference count for an Imgbuf
- */
-void a_Image_imgbuf_ref(void *v_imgbuf)
-{
- ((Imgbuf*)v_imgbuf)->ref();
-}
-
-/*
- * Decrement reference count for an Imgbuf
- */
-void a_Image_imgbuf_unref(void *v_imgbuf)
-{
- ((Imgbuf*)v_imgbuf)->unref();
-}
-
-/*
- * Create a new Imgbuf
- */
-void *a_Image_imgbuf_new(void *v_dw, int img_type, int width, int height)
-{
- Layout *layout = ((Widget*)v_dw)->getLayout();
- if (!layout) {
- MSG_ERR("a_Image_imgbuf_new: layout is NULL.\n");
- exit(1);
- }
- return (void*)layout->createImgbuf(Imgbuf::RGB, width, height);
-}
-
-/*
- * Last reference for this Imgbuf?
- */
-int a_Image_imgbuf_last_reference(void *v_imgbuf)
-{
- return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
+ _MSG("a_Image_close\n");
}
diff --git a/src/image.hh b/src/image.hh
index 73b0e5e7..a66edaae 100644
--- a/src/image.hh
+++ b/src/image.hh
@@ -12,12 +12,24 @@ extern "C" {
#include "bitvec.h"
#include "url.h"
+/*
+ * Defines
+ */
+
+/* Arbitrary maximum for image size (to avoid image size-crafting attacks). */
+#define IMAGE_MAX_AREA (6000 * 6000)
+
+/*
+ * Types
+ */
+
typedef struct _DilloImage DilloImage;
typedef enum {
DILLO_IMG_TYPE_INDEXED,
DILLO_IMG_TYPE_RGB,
DILLO_IMG_TYPE_GRAY,
+ DILLO_IMG_TYPE_CMYK_INV,
DILLO_IMG_TYPE_NOTSET /* Initial value */
} DilloImgType;
@@ -38,11 +50,7 @@ struct _DilloImage {
uint_t width;
uint_t height;
- const uchar_t *cmap; /* Color map (only for indexed) */
- DilloImgType in_type; /* Image Type */
int32_t bg_color; /* Background color */
-
- int ProcessedBytes; /* Amount of bytes already decoded */
bitvec_t *BitVec; /* Bit vector for decoded rows */
uint_t ScanNumber; /* Current decoding scan */
ImageState State; /* Processing status */
@@ -54,24 +62,16 @@ struct _DilloImage {
/*
* Function prototypes
*/
-DilloImage *a_Image_new(int width, int height,
- const char *alt_text, int32_t bg_color);
+DilloImage *a_Image_new(const char *alt_text, int32_t bg_color);
void a_Image_ref(DilloImage *Image);
void a_Image_unref(DilloImage *Image);
void a_Image_set_parms(DilloImage *Image, void *v_imgbuf, DilloUrl *url,
int version, uint_t width, uint_t height,
DilloImgType type);
-void a_Image_set_cmap(DilloImage *Image, const uchar_t *cmap);
-void a_Image_new_scan(DilloImage *image, void *v_imgbuf);
-void a_Image_write(DilloImage *Image, void *v_imgbuf,
- const uchar_t *buf, uint_t y, int decode);
+void a_Image_write(DilloImage *Image, uint_t y);
void a_Image_close(DilloImage *Image);
-void a_Image_imgbuf_ref(void *v_imgbuf);
-void a_Image_imgbuf_unref(void *v_imgbuf);
-void *a_Image_imgbuf_new(void *v_dw, int img_type, int width, int height) ;
-int a_Image_imgbuf_last_reference(void *v_imgbuf);
#ifdef __cplusplus
}
diff --git a/src/imgbuf.cc b/src/imgbuf.cc
new file mode 100644
index 00000000..51f86b74
--- /dev/null
+++ b/src/imgbuf.cc
@@ -0,0 +1,138 @@
+/*
+ * File: imgbuf.cc
+ *
+ * Copyright (C) 2008 Jorge Arellano Cid <jcid@dillo.org>,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "msg.h"
+#include "imgbuf.hh"
+#include "dw/core.hh"
+#include "dw/image.hh"
+
+using namespace dw::core;
+
+/*
+ * Local data
+ */
+static size_t linebuf_size = 0;
+static uchar_t *linebuf = NULL;
+
+
+/*
+ * Decode 'buf' (an image line) into RGB format.
+ */
+static uchar_t *Imgbuf_rgb_line(const uchar_t *buf,
+ DilloImgType type, uchar_t *cmap,
+ uint_t width, uint_t y)
+{
+ uint_t x;
+
+ switch (type) {
+ case DILLO_IMG_TYPE_INDEXED:
+ if (cmap) {
+ for (x = 0; x < width; x++)
+ memcpy(linebuf + x * 3, cmap + buf[x] * 3, 3);
+ } else {
+ MSG_WARN("Gif:: image lacks a color map\n");
+ }
+ break;
+ case DILLO_IMG_TYPE_GRAY:
+ for (x = 0; x < width; x++)
+ memset(linebuf + x * 3, buf[x], 3);
+ break;
+ case DILLO_IMG_TYPE_CMYK_INV:
+ /*
+ * We treat CMYK as if it were "RGBW", and it works. Everyone who is
+ * trying to handle CMYK jpegs is confused by this, and supposedly
+ * the issue is that Adobe CMYK is "wrong" but ubiquitous.
+ */
+ for (x = 0; x < width; x++) {
+ uint_t white = buf[x * 4 + 3];
+ linebuf[x * 3] = buf[x * 4] * white / 0x100;
+ linebuf[x * 3 + 1] = buf[x * 4 + 1] * white / 0x100;
+ linebuf[x * 3 + 2] = buf[x * 4 + 2] * white / 0x100;
+ }
+ break;
+ case DILLO_IMG_TYPE_RGB:
+ /* avoid a memcpy here! --Jcid */
+ return (uchar_t *)buf;
+ case DILLO_IMG_TYPE_NOTSET:
+ MSG_ERR("Imgbuf_rgb_line: type not set...\n");
+ break;
+ }
+ return linebuf;
+}
+
+// Wrappers for Imgbuf -------------------------------------------------------
+
+/*
+ * Increment reference count for an Imgbuf
+ */
+void a_Imgbuf_ref(void *v_imgbuf)
+{
+ ((Imgbuf*)v_imgbuf)->ref();
+}
+
+/*
+ * Decrement reference count for an Imgbuf
+ */
+void a_Imgbuf_unref(void *v_imgbuf)
+{
+ if (v_imgbuf)
+ ((Imgbuf*)v_imgbuf)->unref();
+}
+
+/*
+ * Create a new Imgbuf
+ */
+void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height)
+{
+ Layout *layout = ((Widget*)v_dw)->getLayout();
+ if (!layout) {
+ MSG_ERR("a_Imgbuf_new: layout is NULL.\n");
+ exit(1);
+ }
+ // Assert linebuf is wide enough.
+ if (3 * width > linebuf_size) {
+ linebuf_size = 3 * width;
+ linebuf = (uchar_t*) dRealloc(linebuf, linebuf_size);
+ }
+
+ return (void*)layout->createImgbuf(Imgbuf::RGB, width, height);
+}
+
+/*
+ * Last reference for this Imgbuf?
+ */
+int a_Imgbuf_last_reference(void *v_imgbuf)
+{
+ return ((Imgbuf*)v_imgbuf)->lastReference () ? 1 : 0;
+}
+
+/*
+ * Update the root buffer of an imgbuf.
+ */
+void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
+ uchar_t *cmap, uint_t width, uint_t height, uint_t y)
+
+{
+ dReturn_if_fail ( y < height );
+
+ /* Decode 'buf' and copy it into the imgbuf */
+ uchar_t *newbuf = Imgbuf_rgb_line(buf, type, cmap, width, y);
+ ((Imgbuf*)v_imgbuf)->copyRow(y, (byte *)newbuf);
+}
+
+/*
+ * Reset for a new scan from a multiple-scan image.
+ */
+void a_Imgbuf_new_scan(void *v_imgbuf)
+{
+ ((Imgbuf*)v_imgbuf)->newScan();
+}
+
diff --git a/src/imgbuf.hh b/src/imgbuf.hh
new file mode 100644
index 00000000..9a6e3ff7
--- /dev/null
+++ b/src/imgbuf.hh
@@ -0,0 +1,30 @@
+#ifndef __IMGBUF_HH__
+#define __IMGBUF_HH__
+
+// Imgbuf wrappers
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#include "image.hh"
+
+/*
+ * Function prototypes
+ */
+void a_Imgbuf_ref(void *v_imgbuf);
+void a_Imgbuf_unref(void *v_imgbuf);
+void *a_Imgbuf_new(void *v_dw, int img_type, uint_t width, uint_t height);
+int a_Imgbuf_last_reference(void *v_imgbuf);
+void a_Imgbuf_update(void *v_imgbuf, const uchar_t *buf, DilloImgType type,
+ uchar_t *cmap, uint_t width, uint_t height, uint_t y);
+void a_Imgbuf_new_scan(void *v_imgbuf);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IMGBUF_HH__ */
+
diff --git a/src/jpeg.c b/src/jpeg.c
index 05e4f2d4..262a1346 100644
--- a/src/jpeg.c
+++ b/src/jpeg.c
@@ -32,7 +32,6 @@
#endif
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
#include "capi.h" /* get cache entry status */
@@ -81,57 +80,32 @@ typedef struct DilloJpeg {
/*
* Forward declarations
*/
-static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version);
-static void Jpeg_callback(int Op, CacheClient_t *Client);
static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize);
-static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client);
METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo);
-/* exported function */
-void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
- void **Data);
-
/* this is the routine called by libjpeg when it detects an error. */
METHODDEF(void) Jpeg_errorexit (j_common_ptr cinfo)
{
/* display message and return to setjmp buffer */
my_error_ptr myerr = (my_error_ptr) cinfo->err;
- (*cinfo->err->output_message) (cinfo);
+ if (prefs.show_msg) {
+ DilloJpeg *jpeg =
+ ((my_source_mgr *) ((j_decompress_ptr) cinfo)->src)->jpeg;
+ MSG_WARN("\"%s\": ", URL_STR(jpeg->url));
+ (*cinfo->err->output_message) (cinfo);
+ }
longjmp(myerr->setjmp_buffer, 1);
}
/*
- * MIME handler for "image/jpeg" type
- * (Sets Jpeg_callback or a_Dicache_callback as the cache-client)
+ * Free the jpeg-decoding data structure.
*/
-void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
- void **Data)
+static void Jpeg_free(DilloJpeg *jpeg)
{
- DilloWeb *web = P;
- DICacheEntry *DicEntry;
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, 0);
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Jpeg_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Jpeg_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
+ _MSG("Jpeg_free: jpeg=%p\n", jpeg);
+ jpeg_destroy_decompress(&(jpeg->cinfo));
+ dFree(jpeg);
}
/*
@@ -139,12 +113,17 @@ void *a_Jpeg_image(const char *Type, void *P, CA_Callback_t *Call,
*/
static void Jpeg_close(DilloJpeg *jpeg, CacheClient_t *Client)
{
+ _MSG("Jpeg_close\n");
a_Dicache_close(jpeg->url, jpeg->version, Client);
- jpeg_destroy_decompress(&(jpeg->cinfo));
- dFree(jpeg);
+ Jpeg_free(jpeg);
}
-static void init_source(j_decompress_ptr cinfo)
+/*
+ * The proper signature is:
+ * static void init_source(j_decompress_ptr cinfo)
+ * (declaring it with no parameter avoids a compiler warning)
+ */
+static void init_source()
{
}
@@ -196,14 +175,20 @@ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes)
}
}
-static void term_source(j_decompress_ptr cinfo)
+/*
+ * The proper signature is:
+ * static void term_source(j_decompress_ptr cinfo)
+ * (declaring it with no parameter avoids a compiler warning)
+ */
+static void term_source()
{
}
-static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
{
my_source_mgr *src;
DilloJpeg *jpeg = dMalloc(sizeof(*jpeg));
+ _MSG("a_Jpeg_new: jpeg=%p\n", jpeg);
jpeg->Image = Image;
jpeg->url = url;
@@ -236,12 +221,17 @@ static DilloJpeg *Jpeg_new(DilloImage *Image, DilloUrl *url, int version)
return jpeg;
}
-static void Jpeg_callback(int Op, CacheClient_t *Client)
+void a_Jpeg_callback(int Op, void *data)
{
- if (Op)
- Jpeg_close(Client->CbData, Client);
- else
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
Jpeg_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Jpeg_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Jpeg_free(data);
+ }
}
/*
@@ -278,14 +268,18 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* decompression step 3 (see libjpeg.doc) */
if (jpeg_read_header(&(jpeg->cinfo), TRUE) != JPEG_SUSPENDED) {
type = DILLO_IMG_TYPE_GRAY;
- if (jpeg->cinfo.num_components == 1)
+ if (jpeg->cinfo.num_components == 1) {
type = DILLO_IMG_TYPE_GRAY;
- else if (jpeg->cinfo.num_components == 3)
+ } else if (jpeg->cinfo.num_components == 3) {
type = DILLO_IMG_TYPE_RGB;
- else
- _MSG("jpeg: can't handle %d component images\n",
- jpeg->cinfo.num_components);
-
+ } else {
+ MSG("4-component JPEG!\n");
+ if (jpeg->cinfo.jpeg_color_space == JCS_YCCK)
+ MSG("YCCK. Are the colors wrong?\n");
+ if (!jpeg->cinfo.saw_Adobe_marker)
+ MSG("No adobe marker! Is the image shown in reverse video?\n");
+ type = DILLO_IMG_TYPE_CMYK_INV;
+ }
/*
* If a multiple-scan image is not completely in cache,
* use progressive display, updating as it arrives.
@@ -294,6 +288,17 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
!(a_Capi_get_flags(jpeg->url) & CAPI_Completed))
jpeg->cinfo.buffered_image = TRUE;
+ /* check max image size */
+ if (jpeg->cinfo.image_width <= 0 || jpeg->cinfo.image_height <= 0 ||
+ jpeg->cinfo.image_width >
+ IMAGE_MAX_AREA / jpeg->cinfo.image_height) {
+ MSG("Jpeg_write: suspicious image size request %ux%u\n",
+ (uint_t)jpeg->cinfo.image_width,
+ (uint_t)jpeg->cinfo.image_height);
+ jpeg->state = DILLO_JPEG_ERROR;
+ return;
+ }
+
a_Dicache_set_parms(jpeg->url, jpeg->version, jpeg->Image,
(uint_t)jpeg->cinfo.image_width,
(uint_t)jpeg->cinfo.image_height,
@@ -330,7 +335,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
if (jpeg->state == DILLO_JPEG_READ_BEGIN_SCAN) {
if (jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number)) {
- a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+ a_Dicache_new_scan(jpeg->url, jpeg->version);
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
}
}
@@ -346,8 +351,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* out of input */
break;
}
- a_Dicache_write(jpeg->Image, jpeg->url, jpeg->version,
- linebuf, jpeg->y);
+ a_Dicache_write(jpeg->url, jpeg->version, linebuf, jpeg->y);
jpeg->y++;
@@ -389,7 +393,7 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
/* out of input */
break;
}
- a_Dicache_new_scan(jpeg->Image, jpeg->url, jpeg->version);
+ a_Dicache_new_scan(jpeg->url, jpeg->version);
jpeg->state = DILLO_JPEG_READ_IN_SCAN;
}
}
@@ -398,4 +402,9 @@ static void Jpeg_write(DilloJpeg *jpeg, void *Buf, uint_t BufSize)
}
}
+#else /* ENABLE_JPEG */
+
+void *a_Jpeg_new() { return 0; }
+void a_Jpeg_callback() { return; }
+
#endif /* ENABLE_JPEG */
diff --git a/src/keys.cc b/src/keys.cc
new file mode 100644
index 00000000..1a39f4c8
--- /dev/null
+++ b/src/keys.cc
@@ -0,0 +1,363 @@
+/*
+ * Key parser
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fltk/events.h>
+#include <stdio.h>
+#include <stdlib.h> /* strtol */
+#include <string.h>
+#include <ctype.h>
+
+#include "dlib/dlib.h"
+#include "keys.hh"
+#include "msg.h"
+
+/*
+ * Local data types
+ */
+typedef struct {
+ const char *name;
+ KeysCommand_t cmd;
+ int modifier, key;
+} KeyBinding_t;
+
+typedef struct {
+ const char *name;
+ const int value;
+} Mapping_t;
+
+
+/*
+ * Local data
+ */
+static const Mapping_t keyNames[] = {
+ { "Backspace", fltk::BackSpaceKey },
+ { "Delete", fltk::DeleteKey },
+ { "Down", fltk::DownKey },
+ { "End", fltk::EndKey },
+ { "Esc", fltk::EscapeKey },
+ { "F1", fltk::F1Key },
+ { "F2", fltk::F2Key },
+ { "F3", fltk::F3Key },
+ { "F4", fltk::F4Key },
+ { "F5", fltk::F5Key },
+ { "F6", fltk::F6Key },
+ { "F7", fltk::F7Key },
+ { "F8", fltk::F8Key },
+ { "F9", fltk::F9Key },
+ { "F10", fltk::F10Key },
+ { "F11", fltk::F11Key },
+ { "F12", fltk::F12Key },
+ { "Home", fltk::HomeKey },
+ { "Insert", fltk::InsertKey },
+ { "Left", fltk::LeftKey },
+ { "PageDown", fltk::PageDownKey },
+ { "PageUp", fltk::PageUpKey },
+ { "Print", fltk::PrintKey },
+ { "Return", fltk::ReturnKey },
+ { "Right", fltk::RightKey },
+ { "Space", fltk::SpaceKey },
+ { "Tab", fltk::TabKey },
+ { "Up", fltk::UpKey }
+};
+
+static const Mapping_t modifierNames[] = {
+ { "Shift", fltk::SHIFT },
+ { "Ctrl", fltk::CTRL },
+ { "Alt", fltk::ALT },
+ { "Meta", fltk::META },
+ { "Button1", fltk::BUTTON1 },
+ { "Button2", fltk::BUTTON2 },
+ { "Button3", fltk::BUTTON3 }
+};
+
+static const KeyBinding_t default_keys[] = {
+ { "nop" , KEYS_NOP , 0 , 0 },
+ { "open" , KEYS_OPEN , fltk::CTRL , 'o' },
+ { "new-window" , KEYS_NEW_WINDOW , fltk::CTRL , 'n' },
+ { "new-tab" , KEYS_NEW_TAB , fltk::CTRL , 't' },
+ { "left-tab" , KEYS_LEFT_TAB , fltk::SHIFT , fltk::TabKey },
+ { "right-tab" , KEYS_RIGHT_TAB , fltk::CTRL , fltk::TabKey },
+ { "close-tab" , KEYS_CLOSE_TAB , fltk::CTRL , 'q' },
+ { "find" , KEYS_FIND , fltk::CTRL , 'f' },
+ { "websearch" , KEYS_WEBSEARCH , fltk::CTRL , 's' },
+ { "bookmarks" , KEYS_BOOKMARKS , fltk::CTRL , 'b' },
+ { "fullscreen" , KEYS_FULLSCREEN , fltk::CTRL , fltk::SpaceKey },
+ { "reload" , KEYS_RELOAD , fltk::CTRL , 'r' },
+ { "stop" , KEYS_STOP , 0 , 0 },
+ { "save" , KEYS_SAVE , 0 , 0 },
+ { "hide-panels" , KEYS_HIDE_PANELS , 0 , fltk::EscapeKey },
+ { "file-menu" , KEYS_FILE_MENU , fltk::ALT , 'f' },
+ { "close-all" , KEYS_CLOSE_ALL , fltk::ALT , 'q' },
+ { "back" , KEYS_BACK , 0 , fltk::BackSpaceKey },
+ { "back" , KEYS_BACK , 0 , ',' },
+ { "forward" , KEYS_FORWARD , fltk::SHIFT , fltk::BackSpaceKey },
+ { "forward" , KEYS_FORWARD , 0 , '.' },
+ { "goto" , KEYS_GOTO , fltk::CTRL , 'l' },
+ { "home" , KEYS_HOME , fltk::CTRL , 'h' },
+ { "screen-up" , KEYS_SCREEN_UP , 0 , fltk::PageUpKey },
+ { "screen-up" , KEYS_SCREEN_UP , 0 , 'b' },
+ { "screen-down" , KEYS_SCREEN_DOWN , 0 , fltk::PageDownKey },
+ { "screen-down" , KEYS_SCREEN_DOWN , 0 , fltk::SpaceKey },
+ { "line-up" , KEYS_LINE_UP , 0 , fltk::UpKey },
+ { "line-down" , KEYS_LINE_DOWN , 0 , fltk::DownKey },
+ { "left" , KEYS_LEFT , 0 , fltk::LeftKey },
+ { "right" , KEYS_RIGHT , 0 , fltk::RightKey },
+ { "top" , KEYS_TOP , 0 , fltk::HomeKey },
+ { "bottom" , KEYS_BOTTOM , 0 , fltk::EndKey },
+};
+
+static Dlist *bindings;
+
+
+
+/*
+ * Initialize the bindings list
+ */
+void Keys::init()
+{
+ KeyBinding_t *node;
+
+ // Fill our key bindings list
+ bindings = dList_new(32);
+ for (uint_t i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
+ if (default_keys[i].key) {
+ node = dNew(KeyBinding_t, 1);
+ node->name = dStrdup(default_keys[i].name);
+ node->cmd = default_keys[i].cmd;
+ node->modifier = default_keys[i].modifier;
+ node->key = default_keys[i].key;
+ dList_insert_sorted(bindings, node, nodeByKeyCmp);
+ }
+ }
+}
+
+/*
+ * Free data
+ */
+void Keys::free()
+{
+ KeyBinding_t *node;
+
+ while ((node = (KeyBinding_t*)dList_nth_data(bindings, 0))) {
+ dFree((char*)node->name);
+ dList_remove_fast(bindings, node);
+ dFree(node);
+ }
+ dList_free(bindings);
+}
+
+/*
+ * Compare function by {key,modifier} pairs.
+ */
+int Keys::nodeByKeyCmp(const void *node, const void *key)
+{
+ KeyBinding_t *n = (KeyBinding_t*)node, *k = (KeyBinding_t*)key;
+ _MSG("Keys::nodeByKeyCmp modifier=%d\n", k->modifier);
+ return (n->key != k->key) ? (n->key - k->key) : (n->modifier - k->modifier);
+}
+
+/*
+ * Look if the just pressed key is bound to a command.
+ * Return value: The command if found, KEYS_NOP otherwise.
+ */
+KeysCommand_t Keys::getKeyCmd()
+{
+ KeysCommand_t ret = KEYS_NOP;
+ KeyBinding_t keyNode;
+ // We're only interested in some flags
+ keyNode.modifier = fltk::event_state() &
+ (fltk::SHIFT | fltk::CTRL | fltk::ALT | fltk::META);
+
+ if (keyNode.modifier == fltk::SHIFT &&
+ ispunct(fltk::event_text()[0])) {
+ // Get key code for a shifted character
+ keyNode.key = fltk::event_text()[0];
+ keyNode.modifier = 0;
+ } else {
+ keyNode.key = fltk::event_key();
+ }
+
+ _MSG("getKeyCmd: key=%d, mod=%d\n", keyNode.key, keyNode.modifier);
+ void *data = dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
+ if (data)
+ ret = ((KeyBinding_t*)data)->cmd;
+ return ret;
+}
+
+/*
+ * Remove a key binding from the table.
+ */
+void Keys::delKeyCmd(int key, int mod)
+{
+ KeyBinding_t keyNode, *node;
+ keyNode.key = key;
+ keyNode.modifier = mod;
+
+ node = (KeyBinding_t*) dList_find_sorted(bindings, &keyNode, nodeByKeyCmp);
+ if (node) {
+ dList_remove(bindings, node);
+ dFree((char*)node->name);
+ dFree(node);
+ }
+}
+
+/*
+ * Takes a key name and looks it up in the mapping table. If
+ * found, its key code is returned. Otherwise -1 is given
+ * back.
+ */
+int Keys::getKeyCode(char *keyName)
+{
+ uint_t i;
+ for (i = 0; i < sizeof(keyNames) / sizeof(Mapping_t); i++) {
+ if (!dStrcasecmp(keyNames[i].name, keyName)) {
+ return keyNames[i].value;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Takes a command name and searches it in the mapping table.
+ * Return value: command code if found, -1 otherwise
+ */
+KeysCommand_t Keys::getCmdCode(const char *commandName)
+{
+ uint_t i;
+
+ for (i = 0; i < sizeof(default_keys) / sizeof(KeyBinding_t); i++) {
+ if (!dStrcasecmp(default_keys[i].name, commandName))
+ return default_keys[i].cmd;
+ }
+ return KEYS_INVALID;
+}
+
+/*
+ * Takes a modifier name and looks it up in the mapping table. If
+ * found, its key code is returned. Otherwise -1 is given back.
+ */
+int Keys::getModifier(char *modifierName)
+{
+ uint_t i;
+ for (i = 0; i < sizeof(modifierNames) / sizeof(Mapping_t); i++) {
+ if (!dStrcasecmp(modifierNames[i].name, modifierName)) {
+ return modifierNames[i].value;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Given a keys command, return a shortcut for it, or 0 if there is none
+ * (e.g., for KEYS_NEW_WINDOW, return CTRL+'n').
+ */
+int Keys::getShortcut(KeysCommand_t cmd)
+{
+ int len = dList_length(bindings);
+
+ for (int i = 0; i < len; i++) {
+ KeyBinding_t *node = (KeyBinding_t*)dList_nth_data(bindings, i);
+ if (cmd == node->cmd)
+ return node->modifier + node->key;
+ }
+ return 0;
+}
+
+/*
+ * Parse a key-combination/command-name pair, and
+ * insert it into the bindings list.
+ */
+void Keys::parseKey(char *key, char *commandName)
+{
+ char *p, *modstr, *keystr;
+ KeysCommand_t symcode;
+ int st, keymod = 0, keycode = 0;
+
+ _MSG("Keys::parseKey key='%s' commandName='%s'\n", key, commandName);
+
+ // Get command code
+ if ((symcode = getCmdCode(commandName)) == KEYS_INVALID) {
+ MSG("Keys::parseKey: Invalid command name: '%s'\n", commandName);
+ return;
+ }
+
+ // Skip space
+ for ( ; isspace(*key); ++key) ;
+ // Get modifiers
+ while(*key == '<' && (p = strchr(key, '>'))) {
+ ++key;
+ modstr = dStrndup(key, p - key);
+ if ((st = getModifier(modstr)) == -1) {
+ MSG("Keys::parseKey unknown modifier: %s\n", modstr);
+ } else {
+ keymod |= st;
+ }
+ dFree(modstr);
+ key = p + 1;
+ }
+ // Allow trailing space after keyname
+ keystr = (*key && (p = strchr(key + 1, ' '))) ? dStrndup(key, p - key - 1) :
+ dStrdup(key);
+ // Get key code
+ if (!key[1]) {
+ keycode = *key;
+ } else if (key[0] == '0' && key[1] == 'x') {
+ /* keysym. For details on values reported, see fltk's fltk/events.h */
+ keycode = strtol(key, NULL, 0x10);
+ } else if ((st = getKeyCode(keystr)) == -1) {
+ MSG("Keys::parseKey unknown keyname: %s\n", keystr);
+ } else {
+ keycode = st;
+ }
+ dFree(keystr);
+
+ // Set binding
+ if (keycode) {
+ delKeyCmd(keycode, keymod);
+ if (symcode != KEYS_NOP) {
+ KeyBinding_t *node = dNew(KeyBinding_t, 1);
+ node->name = dStrdup(commandName);
+ node->cmd = symcode;
+ node->modifier = keymod;
+ node->key = keycode;
+ dList_insert_sorted(bindings, node, nodeByKeyCmp);
+ _MSG("parseKey: Adding key=%d, mod=%d\n", node->key, node->modifier);
+ }
+ }
+}
+
+/*
+ * Parse the keysrc.
+ */
+void Keys::parse(FILE *fp)
+{
+ char *line, *keycomb, *command;
+ int st, lineno = 1;
+
+ // scan the file line by line
+ while ((line = dGetline(fp)) != NULL) {
+ st = dParser_parse_rc_line(&line, &keycomb, &command);
+
+ if (st == 0) {
+ _MSG("Keys::parse: keycomb=%s, command=%s\n", keycomb, command);
+ parseKey(keycomb, command);
+ } else if (st < 0) {
+ MSG("Keys::parse: Syntax error in keysrc line %d: "
+ "keycomb=\"%s\" command=\"%s\"\n", lineno, keycomb, command);
+ }
+
+ dFree(line);
+ ++lineno;
+ }
+ fclose(fp);
+}
diff --git a/src/keys.hh b/src/keys.hh
new file mode 100644
index 00000000..d234838c
--- /dev/null
+++ b/src/keys.hh
@@ -0,0 +1,68 @@
+/*
+ * Key parser
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __KEYS_HH__
+#define __KEYS_HH__
+
+
+typedef enum {
+ KEYS_INVALID = -1,
+ KEYS_NOP, /* No operation bound */
+ KEYS_OPEN,
+ KEYS_NEW_WINDOW,
+ KEYS_NEW_TAB,
+ KEYS_LEFT_TAB,
+ KEYS_RIGHT_TAB,
+ KEYS_CLOSE_TAB,
+ KEYS_FIRST_TAB,
+ KEYS_LAST_TAB,
+ KEYS_FIND,
+ KEYS_WEBSEARCH,
+ KEYS_BOOKMARKS,
+ KEYS_FULLSCREEN,
+ KEYS_RELOAD,
+ KEYS_STOP,
+ KEYS_SAVE,
+ KEYS_HIDE_PANELS,
+ KEYS_FILE_MENU,
+ KEYS_CLOSE_ALL,
+ KEYS_BACK,
+ KEYS_FORWARD,
+ KEYS_GOTO,
+ KEYS_HOME,
+ KEYS_SCREEN_UP,
+ KEYS_SCREEN_DOWN,
+ KEYS_LINE_UP,
+ KEYS_LINE_DOWN,
+ KEYS_LEFT,
+ KEYS_RIGHT,
+ KEYS_TOP,
+ KEYS_BOTTOM
+} KeysCommand_t;
+
+class Keys {
+private:
+ static int nodeByKeyCmp(const void *node, const void *key);
+ static void delKeyCmd(int key, int mod);
+ static KeysCommand_t getCmdCode(const char *symbolName);
+ static int getKeyCode(char *keyName);
+ static int getModifier(char *modifierName);
+ static void parseKey(char *key, char *symbol);
+public:
+ static void init();
+ static void free();
+ static void parse(FILE *fp);
+ static KeysCommand_t getKeyCmd(void);
+ static int getShortcut(KeysCommand_t cmd);
+};
+
+
+#endif /* __KEYS_HH__ */
diff --git a/src/keysrc b/src/keysrc
new file mode 100644
index 00000000..3f53674f
--- /dev/null
+++ b/src/keysrc
@@ -0,0 +1,101 @@
+# keysrc
+# Sample dillo key bindings file.
+#
+# The format is: "key = action" or "<modifier>key = action".
+# Lines that begin with a '#' are comments.
+# The commented-out bindings below show the defaults built into Dillo.
+#
+# Modifiers recognized: "Shift", "Ctrl", "Alt", "Meta".
+# Key names recognized: "Backspace", "Delete", "Down", "End", "Esc",
+# "F1" through "F12", "Home", "Insert", "Left", "PageDown", "PageUp",
+# "Print", "Return", "Right", "Space", "Tab", "Up".
+#
+# If Dillo is running under X11, keys whose names are not recognized can
+# be specified using their keysym value in hexadecimal. Use xev to get
+# the keysym. Example rule: "0x1008ff27 = forward".
+#
+# The action "nop" (no operation) can be used to remove a binding.
+
+# "open" lets you browse your local files for one to open.
+#<ctrl>o = open
+
+# "new-window" opens a new browser window.
+#<ctrl>n = new-window
+
+# "new-tab" opens a new tab in the current browser window.
+#<ctrl>t = new-tab
+
+# "close-tab" closes the current tab.
+# Note that this closes the browser window if there is only one tab.
+#<ctrl>q = close-tab
+
+# "close-all" closes all tabs/windows and exits.
+#<alt>q = close-all
+
+# "left-tab" and "right-tab" switch to the left/right of the current tab.
+# *** NOT HOOKED UP YET ***
+# <shift>tab = left-tab
+# <ctrl>tab = right-tab
+
+# "back" and "forward" move back/forward through the browser history.
+#backspace = back
+#<shift>backspace = forward
+#, = back
+#. = forward
+
+# "reload" the current page.
+#<ctrl>r = reload
+
+# "home" goes to the homepage that you set in your dillorc.
+#<ctrl>h = home
+
+# "find" lets you search for a text string on the current page.
+#<ctrl>f = find
+
+# "hide-panels" hides the findbar.
+#esc = hide-panels
+
+# "websearch" lets you send a text string to the search engine that you
+# set in your dillorc.
+#<ctrl>s = websearch
+
+# go to your "bookmarks".
+#<ctrl>b = bookmarks
+
+# "fullscreen" hides/shows the panels at the top and bottom of a dillo window.
+#<ctrl>space = fullscreen
+
+# "file-menu" pops up the file menu.
+#<alt>f = file-menu
+
+# "goto" goes to the location bar at the top of the window.
+#<ctrl>l = goto
+
+# "stop" loading the page.
+#(stop has no default binding)
+
+# "save" the current page.
+#(save has no default binding)
+
+#--------------------------------------------------------------------
+# MOTION COMMANDS
+#--------------------------------------------------------------------
+
+#pageup = screen-up
+#b = screen-up
+
+#pagedown = screen-down
+#space = screen-down
+
+#up = line-up
+
+#down = line-down
+
+#left = left
+
+#right = right
+
+#home = top
+
+#end = bottom
+
diff --git a/src/klist.c b/src/klist.c
index a0652499..813269a3 100644
--- a/src/klist.c
+++ b/src/klist.c
@@ -117,9 +117,10 @@ void a_Klist_free(Klist_t **KlistPtr)
while (dList_length(Klist->List) > 0) {
node = dList_nth_data(Klist->List, 0);
- dList_remove_fast(Klist->List, 0);
+ dList_remove_fast(Klist->List, node);
dFree(node);
}
+ dList_free(Klist->List);
dFree(Klist);
*KlistPtr = NULL;
}
diff --git a/src/menu.cc b/src/menu.cc
index 6856143e..d802bddb 100644
--- a/src/menu.cc
+++ b/src/menu.cc
@@ -11,19 +11,20 @@
// Functions/Methods for menus
-#include <stdio.h>
-#include <stdarg.h>
#include <fltk/events.h>
#include <fltk/PopupMenu.h>
#include <fltk/Item.h>
+#include <fltk/ToggleItem.h>
#include <fltk/Divider.h>
+#include "lout/misc.hh" /* SimpleVector */
#include "msg.h"
#include "menu.hh"
#include "uicmd.hh"
#include "history.h"
#include "html.hh"
#include "ui.hh" // for (UI *)
+#include "keys.hh"
#include "timeout.hh"
using namespace fltk;
@@ -38,9 +39,9 @@ static DilloUrl *popup_url = NULL;
// Weak reference to the popup's bw
static BrowserWindow *popup_bw = NULL;
// Where to place the filemenu popup
-int popup_x, popup_y;
+static int popup_x, popup_y;
// History popup direction (-1 = back, 1 = forward).
-static int history_direction = -1;
+static int history_direction = -1;
// History popup, list of URL-indexes.
static int *history_list = NULL;
@@ -66,7 +67,7 @@ public:
* TODO: erase the URL on popup close.
*/
void CustItem::draw() {
- DilloUrl *url;
+ const DilloUrl *url;
if (flags() & SELECTED) {
url = a_History_get_url(history_list[(VOIDP2INT(user_data()))-1]);
@@ -84,7 +85,7 @@ static void filemenu_cb(Widget *wid, void *data)
{
if (strcmp((char*)data, "nw") == 0) {
UI *ui = (UI*)popup_bw->ui;
- a_UIcmd_browser_window_new(ui->w(), ui->h(), popup_bw);
+ a_UIcmd_browser_window_new(ui->w(), ui->h(), 0, popup_bw);
} else if (strcmp((char*)data, "nt") == 0) {
a_UIcmd_open_url_nt(popup_bw, NULL, 1);
} else if (strcmp((char*)data, "of") == 0) {
@@ -108,22 +109,22 @@ static void Menu_copy_urlstr_cb(Widget *)
static void Menu_link_cb(Widget*, void *user_data)
{
DilloUrl *url = (DilloUrl *) user_data ;
- MSG("Menu_link_cb: click! :-)\n");
+ _MSG("Menu_link_cb: click! :-)\n");
if (url)
a_Menu_link_popup(popup_bw, url);
}
-/*
+/*
* Open URL
*/
static void Menu_open_url_cb(Widget* )
{
- MSG("Open URL cb: click! :-)\n");
+ _MSG("Open URL cb: click! :-)\n");
a_UIcmd_open_url(popup_bw, popup_url);
}
-/*
+/*
* Open URL in new window
*/
static void Menu_open_url_nw_cb(Widget* )
@@ -132,7 +133,7 @@ static void Menu_open_url_nw_cb(Widget* )
a_UIcmd_open_url_nw(popup_bw, popup_url);
}
-/*
+/*
* Open URL in new Tab
*/
static void Menu_open_url_nt_cb(Widget* )
@@ -142,7 +143,7 @@ static void Menu_open_url_nt_cb(Widget* )
a_UIcmd_open_url_nt(popup_bw, popup_url, focus);
}
-/*
+/*
* Add bookmark
*/
static void Menu_add_bookmark_cb(Widget* )
@@ -150,7 +151,7 @@ static void Menu_add_bookmark_cb(Widget* )
a_UIcmd_add_bookmark(popup_bw, popup_url);
}
-/*
+/*
* Find text
*/
static void Menu_find_text_cb(Widget* )
@@ -158,7 +159,7 @@ static void Menu_find_text_cb(Widget* )
((UI *)popup_bw->ui)->set_findbar_visibility(1);
}
-/*
+/*
* Save link
*/
static void Menu_save_link_cb(Widget* )
@@ -166,7 +167,7 @@ static void Menu_save_link_cb(Widget* )
a_UIcmd_save_link(popup_bw, popup_url);
}
-/*
+/*
* Save current page
*/
static void Menu_save_page_cb(Widget* )
@@ -174,15 +175,15 @@ static void Menu_save_page_cb(Widget* )
a_UIcmd_save(popup_bw);
}
-/*
- * Save current page
+/*
+ * View current page source
*/
static void Menu_view_page_source_cb(Widget* )
{
- a_UIcmd_view_page_source(popup_url);
+ a_UIcmd_view_page_source(popup_bw, popup_url);
}
-/*
+/*
* View current page's bugs
*/
static void Menu_view_page_bugs_cb(Widget* )
@@ -192,23 +193,58 @@ static void Menu_view_page_bugs_cb(Widget* )
/*
* Load images on current page that match URL pattern
- *
- * BUG: assumes that the document is a DilloHtml.
*/
static void Menu_load_images_cb(Widget*, void *user_data)
{
- DilloUrl *pattern = (DilloUrl *) user_data ;
+ DilloUrl *page_url = (DilloUrl *) user_data;
+ void *doc = a_Bw_get_url_doc(popup_bw, page_url);
- if (popup_bw && popup_bw->Docs) {
- int i, n;
- n = dList_length(popup_bw->Docs);
- for (i = 0; i < n; i++) {
- a_Html_load_images(dList_nth_data(popup_bw->Docs, i), pattern);
- }
- }
+ if (doc)
+ a_Html_load_images(doc, popup_url);
+}
+
+/*
+ * Submit form
+ */
+static void Menu_form_submit_cb(Widget*, void *v_form)
+{
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_submit(doc, v_form);
+}
+
+/*
+ * Reset form
+ */
+static void Menu_form_reset_cb(Widget*, void *v_form)
+{
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_reset(doc, v_form);
+}
+
+/*
+ * Toggle display of 'hidden' form controls.
+ */
+static void Menu_form_hiddens_cb(Widget *w, void *user_data)
+{
+ void *v_form = w->parent()->user_data();
+ bool visible = *((bool *) user_data);
+ void *doc = a_Bw_get_url_doc(popup_bw, popup_url);
+
+ if (doc)
+ a_Html_form_display_hiddens(doc, v_form, !visible);
+}
+
+static void Menu_stylesheet_cb(Widget *w, void *vUrl)
+{
+ const DilloUrl *url = (const DilloUrl *) vUrl;
+ a_UIcmd_open_url(popup_bw, url);
}
-/*
+/*
* Validate URL with the W3C
*/
static void Menu_bugmeter_validate_w3c_cb(Widget* )
@@ -221,7 +257,7 @@ static void Menu_bugmeter_validate_w3c_cb(Widget* )
dStr_free(dstr, 1);
}
-/*
+/*
* Validate URL with the WDG
*/
static void Menu_bugmeter_validate_wdg_cb(Widget* )
@@ -235,7 +271,7 @@ static void Menu_bugmeter_validate_wdg_cb(Widget* )
dStr_free(dstr, 1);
}
-/*
+/*
* Show info page for the bug meter
*/
static void Menu_bugmeter_about_cb(Widget* )
@@ -251,7 +287,7 @@ static void Menu_history_cb(Widget *wid, void *data)
{
int mb = ((CustItem*)wid)->button();
int offset = history_direction * VOIDP2INT(data);
- DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
+ const DilloUrl *url = a_History_get_url(history_list[VOIDP2INT(data)-1]);
if (mb == 2) {
// Middle button, open in a new window/tab
@@ -293,38 +329,41 @@ static void Menu_popup_cb2(void *data)
/*
* Page popup menu (construction & popup)
*/
-void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs)
+void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
+ bool_t has_bugs, void *v_cssUrls)
{
+ lout::misc::SimpleVector <DilloUrl*> *cssUrls =
+ (lout::misc::SimpleVector <DilloUrl*> *) v_cssUrls;
+ Item *i;
+ int j;
// One menu for every browser window
static PopupMenu *pm = 0;
// Active/inactive control.
- static Item *view_page_bugs_item = 0;
- static Item *load_images_item = 0;
+ static Item *view_page_bugs_item = 0, *view_source_item = 0;
+ static ItemGroup *stylesheets = 0;
popup_bw = bw;
a_Url_free(popup_url);
popup_url = a_Url_dup(url);
if (!pm) {
- Item *i;
pm = new PopupMenu(0,0,0,0,"&PAGE OPTIONS");
pm->begin();
- i = new Item("View page Source");
+ i = view_source_item = new Item("View page Source");
i->callback(Menu_view_page_source_cb);
i = view_page_bugs_item = new Item("View page Bugs");
i->callback(Menu_view_page_bugs_cb);
- i = load_images_item = new Item("Load images");
- i->callback(Menu_load_images_cb);
+ stylesheets = new ItemGroup("View Stylesheets");
+ new Divider();
i = new Item("Bookmark this page");
i->callback(Menu_add_bookmark_cb);
- new Divider();
+ new Divider();
i = new Item("Find Text");
i->callback(Menu_find_text_cb);
//i->shortcut(CTRL+'f');
i = new Item("Jump to...");
i->deactivate();
- new Divider();
+ new Divider();
i = new Item("Save page As...");
i->callback(Menu_save_page_cb);
@@ -337,11 +376,49 @@ void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
else
view_page_bugs_item->deactivate();
- if (unloaded_imgs == TRUE) {
- load_images_item->activate();
- load_images_item->user_data(NULL); /* wildcard */
+ if (strncmp(URL_STR(url), "dpi:/vsource/", 13) == 0)
+ view_source_item->deactivate();
+ else
+ view_source_item->activate();
+
+ int n = stylesheets->children();
+ for (j = 0; j < n; j++) {
+ /* get rid of the old ones */
+ Widget *child = stylesheets->child(0);
+ dFree((char *)child->label());
+ a_Url_free((DilloUrl *)child->user_data());
+ delete child;
+ }
+
+ if (cssUrls && cssUrls->size () > 0) {
+ stylesheets->activate();
+ for (j = 0; j < cssUrls->size(); j++) {
+ /* may want ability to Load individual unloaded stylesheets as well */
+ const char *action = "View ";
+ DilloUrl *url = cssUrls->get(j);
+ const char *url_str = URL_STR(url);
+ const uint_t head_length = 30, tail_length = 40,
+ url_len = strlen(url_str);;
+ char *label;
+
+ if (url_len > head_length + tail_length + 3) {
+ /* trim long URLs when making the label */
+ char *url_head = dStrndup(url_str, head_length);
+ const char *url_tail = url_str + (url_len - tail_length);
+ label = dStrconcat(action, url_head, "...", url_tail, NULL);
+ dFree(url_head);
+ } else {
+ label = dStrconcat(action, url_str, NULL);
+ }
+
+ i = new Item(label);
+ i->set_flag(RAW_LABEL);
+ i->user_data(a_Url_dup(url));
+ i->callback(Menu_stylesheet_cb);
+ stylesheets->add(i);
+ }
} else {
- load_images_item->deactivate();
+ stylesheets->deactivate();
}
a_Timeout_add(0.0, Menu_popup_cb, (void *)pm);
@@ -367,12 +444,12 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
i->callback(Menu_open_url_nw_cb);
i = new Item("Open Link in New Tab");
i->callback(Menu_open_url_nt_cb);
- new Divider();
+ new Divider();
i = new Item("Bookmark this Link");
i->callback(Menu_add_bookmark_cb);
i = new Item("Copy Link location");
i->callback(Menu_copy_urlstr_cb);
- new Divider();
+ new Divider();
i = new Item("Save Link As...");
i->callback(Menu_save_link_cb);
@@ -387,21 +464,25 @@ void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url)
* Image popup menu (construction & popup)
*/
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t loaded_img, DilloUrl *link_url)
+ bool_t loaded_img, DilloUrl *page_url,
+ DilloUrl *link_url)
{
// One menu for every browser window
static PopupMenu *pm = 0;
// Active/inactive control.
static Item *link_menuitem = 0;
static Item *load_img_menuitem = 0;
+ static DilloUrl *popup_page_url = NULL;
static DilloUrl *popup_link_url = NULL;
popup_bw = bw;
a_Url_free(popup_url);
popup_url = a_Url_dup(url);
+ a_Url_free(popup_page_url);
+ popup_page_url = a_Url_dup(page_url);
a_Url_free(popup_link_url);
popup_link_url = a_Url_dup(link_url);
-
+
if (!pm) {
Item *i;
pm = new PopupMenu(0,0,0,0,"&IMAGE OPTIONS");
@@ -434,7 +515,7 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
load_img_menuitem->deactivate();
} else {
load_img_menuitem->activate();
- load_img_menuitem->user_data(popup_url);
+ load_img_menuitem->user_data(popup_page_url);
}
if (link_url) {
@@ -448,6 +529,39 @@ void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
}
/*
+ * Form popup menu (construction & popup)
+ */
+void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
+ void *formptr, bool_t hidvis)
+{
+ static PopupMenu *pm = 0;
+ static Item *hiddens_item = 0;
+ static bool hiddens_visible;
+
+ popup_bw = bw;
+ a_Url_free(popup_url);
+ popup_url = a_Url_dup(page_url);
+ if (!pm) {
+ Item *i;
+ pm = new PopupMenu(0,0,0,0,"FORM OPTIONS");
+ pm->add(i = new Item("Submit form"));
+ i->callback(Menu_form_submit_cb);
+ pm->add(i = new Item("Reset form"));
+ i->callback(Menu_form_reset_cb);
+ pm->add(hiddens_item = new Item(""));
+ hiddens_item->callback(Menu_form_hiddens_cb);
+ hiddens_item->user_data(&hiddens_visible);
+ pm->type(PopupMenu::POPUP123);
+ }
+ pm->user_data(formptr);
+
+ hiddens_visible = hidvis;
+ hiddens_item->label(hiddens_visible ? "Hide hiddens": "Show hiddens");
+
+ a_Timeout_add(0.0, Menu_popup_cb, (void *)pm);
+}
+
+/*
* File popup menu (construction & popup)
*/
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
@@ -466,21 +580,29 @@ void a_Menu_file_popup(BrowserWindow *bw, void *v_wid)
popup_url = NULL;
if (!pm) {
+ int shortcut;
Item *i;
pm = new PopupMenu(0,0,0,0,"File");
pm->begin();
- i = new Item("New Window", CTRL+'n', filemenu_cb, (void*)"nw");
- i = new Item("New Tab", CTRL+'t', filemenu_cb, (void*)"nt");
+ shortcut = Keys::getShortcut(KEYS_NEW_WINDOW);
+ i = new Item("New Window", shortcut, filemenu_cb, (void*)"nw");
+ shortcut = Keys::getShortcut(KEYS_NEW_TAB);
+ i = new Item("New Tab", shortcut, filemenu_cb, (void*)"nt");
new Divider();
- i = new Item("Open File...", CTRL+'o', filemenu_cb, (void*)"of");
- i = new Item("Open URL...", CTRL+'l', filemenu_cb, (void*)"ou");
- i = new Item("Close", CTRL+'q', filemenu_cb, (void*)"cw");
+ shortcut = Keys::getShortcut(KEYS_OPEN);
+ i = new Item("Open File...", shortcut, filemenu_cb, (void*)"of");
+ shortcut = Keys::getShortcut(KEYS_GOTO);
+ i = new Item("Open URL...", shortcut, filemenu_cb, (void*)"ou");
+ shortcut = Keys::getShortcut(KEYS_CLOSE_TAB);
+ i = new Item("Close", shortcut, filemenu_cb, (void*)"cw");
new Divider();
- i = new Item("Exit Dillo", ALT+'q', filemenu_cb, (void*)"ed");
+ shortcut = Keys::getShortcut(KEYS_CLOSE_ALL);
+ i = new Item("Exit Dillo", shortcut, filemenu_cb, (void*)"ed");
pm->type(PopupMenu::POPUP123);
pm->end();
}
+ pm->label(wid->visible() ? NULL : "File");
a_Timeout_add(0.0, Menu_popup_cb2, (void *)pm);
}
@@ -503,7 +625,7 @@ void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
i->callback(Menu_bugmeter_validate_w3c_cb);
i = new Item("Validate URL with WDG");
i->callback(Menu_bugmeter_validate_wdg_cb);
- new Divider();
+ new Divider();
i = new Item("About Bug Meter...");
i->callback(Menu_bugmeter_about_cb);
pm->type(PopupMenu::POPUP123);
@@ -520,7 +642,6 @@ void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url)
*/
void a_Menu_history_popup(BrowserWindow *bw, int direction)
{
- // One menu for every browser window
static PopupMenu *pm = 0;
Item *it;
int i;
@@ -547,7 +668,7 @@ void a_Menu_history_popup(BrowserWindow *bw, int direction)
for (i = 0; history_list[i] != -1; i += 1) {
// TODO: restrict title size
it = new CustItem(a_History_get_title(history_list[i], 1));
- it->callback(Menu_history_cb, (void*)(i+1));
+ it->callback(Menu_history_cb, INT2VOIDP(i+1));
}
pm->type(PopupMenu::POPUP123);
pm->end();
@@ -555,16 +676,70 @@ void a_Menu_history_popup(BrowserWindow *bw, int direction)
pm->popup();
}
+/*
+ * Toggle use of remote stylesheets
+ */
+static void Menu_remote_css_cb(Widget *wid)
+{
+ _MSG("Menu_remote_css_cb\n");
+ prefs.load_stylesheets = wid->state() ? 1 : 0;
+ a_UIcmd_repush(popup_bw);
+}
-void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url) { }
-void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url) { }
-void a_Menu_popup_clear_url2(void *menu_popup) { }
+/*
+ * Toggle use of embedded CSS style
+ */
+static void Menu_embedded_css_cb(Widget *wid)
+{
+ prefs.parse_embedded_css = wid->state() ? 1 : 0;
+ a_UIcmd_repush(popup_bw);
+}
+
+/*
+ * Toggle loading of images -- and load them if enabling.
+ */
+static void Menu_imgload_toggle_cb(Widget *wid)
+{
+ if ((prefs.load_images = wid->state() ? 1 : 0)) {
+ void *doc = a_Bw_get_current_doc(popup_bw);
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw) { return NULL; }
+ if (doc) {
+ DilloUrl *pattern = NULL;
+ a_Html_load_images(doc, pattern);
+ }
+ }
+}
-void a_Menu_pagemarks_new (BrowserWindow *bw) { }
-void a_Menu_pagemarks_destroy (BrowserWindow *bw) { }
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
- int level) { }
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str) { }
+/*
+ * Tools popup menu (construction & popup)
+ */
+void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid)
+{
+ // One menu shared by every browser window
+ static PopupMenu *pm = NULL;
+ Widget *wid = (Widget*)v_wid;
+ Item *it;
+
+ popup_bw = bw;
+
+ if (!pm) {
+ pm = new PopupMenu(0,0,0,0, "TOOLS");
+ pm->begin();
+ it = new ToggleItem("Use remote CSS");
+ it->callback(Menu_remote_css_cb);
+ it->state(prefs.load_stylesheets);
+ it = new ToggleItem("Use embedded CSS");
+ it->callback(Menu_embedded_css_cb);
+ it->state(prefs.parse_embedded_css);
+ new Divider();
+ it = new ToggleItem("Load images");
+ it->callback(Menu_imgload_toggle_cb);
+ it->state(prefs.load_images);
+ pm->type(PopupMenu::POPUP13);
+ pm->end();
+ }
+ //pm->popup();
+ pm->value(-1);
+ ((Menu*)pm)->popup(Rectangle(0,wid->h(),pm->w(),pm->h()));
+}
diff --git a/src/menu.hh b/src/menu.hh
index 9e8a73c6..668de001 100644
--- a/src/menu.hh
+++ b/src/menu.hh
@@ -8,26 +8,17 @@ extern "C" {
#endif /* __cplusplus */
void a_Menu_page_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs);
+ bool_t has_bugs, void *v_cssUrls);
void a_Menu_link_popup(BrowserWindow *bw, const DilloUrl *url);
void a_Menu_image_popup(BrowserWindow *bw, const DilloUrl *url,
- bool_t loaded_img, DilloUrl *link_url);
+ bool_t loaded_img, DilloUrl *page_url,
+ DilloUrl *link_url);
+void a_Menu_form_popup(BrowserWindow *bw, const DilloUrl *page_url,
+ void *vform, bool_t showing_hiddens);
void a_Menu_file_popup(BrowserWindow *bw, void *v_wid);
void a_Menu_bugmeter_popup(BrowserWindow *bw, const DilloUrl *url);
void a_Menu_history_popup(BrowserWindow *bw, int direction);
-
-//---------------------
-void a_Menu_popup_set_url(BrowserWindow *bw, const DilloUrl *url);
-void a_Menu_popup_set_url2(BrowserWindow *bw, const DilloUrl *url);
-void a_Menu_popup_clear_url2(void *menu_popup);
-
-DilloUrl *a_Menu_popup_get_url(BrowserWindow *bw);
-
-void a_Menu_pagemarks_new (BrowserWindow *bw);
-void a_Menu_pagemarks_destroy (BrowserWindow *bw);
-void a_Menu_pagemarks_add(BrowserWindow *bw, void *page, void *style,
- int level);
-void a_Menu_pagemarks_set_text(BrowserWindow *bw, const char *str);
+void a_Menu_tools_popup(BrowserWindow *bw, void *v_wid);
#ifdef __cplusplus
diff --git a/src/misc.c b/src/misc.c
index 646a79dc..9ba8c9b9 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -12,21 +12,21 @@
#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
#include <ctype.h>
+#include <assert.h>
+#include "utf8.hh"
#include "msg.h"
#include "misc.h"
-
/*
* Escape characters as %XX sequences.
* Return value: New string.
*/
char *a_Misc_escape_chars(const char *str, const char *esc_set)
{
- static const char *hex = "0123456789ABCDEF";
+ static const char *const hex = "0123456789ABCDEF";
char *p = NULL;
Dstr *dstr;
int i;
@@ -47,34 +47,54 @@ char *a_Misc_escape_chars(const char *str, const char *esc_set)
return p;
}
-
#define TAB_SIZE 8
/*
* Takes a string and converts any tabs to spaces.
*/
-char *a_Misc_expand_tabs(const char *str, int len)
+int
+a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen)
{
- Dstr *New = dStr_new("");
- int i, j, pos, old_pos;
- char *val;
-
- if (len) {
- for (pos = 0, i = 0; i < len; i++) {
- if (str[i] == '\t') {
- /* Fill with whitespaces until the next tab. */
- old_pos = pos;
- pos += TAB_SIZE - (pos % TAB_SIZE);
- for (j = old_pos; j < pos; j++)
- dStr_append_c(New, ' ');
- } else {
- dStr_append_c(New, str[i]);
- pos++;
- }
+ int j, pos = 0, written = 0, old_pos, char_len;
+ uint_t code;
+ static const int combining_char_space = 32;
+
+ while (*start < end && written < buflen - TAB_SIZE - combining_char_space) {
+ code = a_Utf8_decode(*start, end, &char_len);
+
+ if (code == '\t') {
+ /* Fill with whitespaces until the next tab. */
+ old_pos = pos;
+ pos += TAB_SIZE - (pos % TAB_SIZE);
+ for (j = old_pos; j < pos; j++)
+ buf[written++] = ' ';
+ } else {
+ assert(char_len <= 4);
+ for (j = 0; j < char_len; j++)
+ buf[written++] = (*start)[j];
+ pos++;
}
+
+ *start += char_len;
}
- val = New->str;
- dStr_free(New, FALSE);
- return val;
+
+ /* If following chars are combining chars (e.g. accents) add them to the
+ * buffer. We have reserved combining_char_space bytes for this.
+ * If there should be more combining chars, we split nevertheless.
+ */
+ while (*start < end && written < buflen - 4) {
+ code = a_Utf8_decode(*start, end, &char_len);
+
+ if (! a_Utf8_combining_char(code))
+ break;
+
+ assert(char_len <= 4);
+ for (j = 0; j < char_len; j++)
+ buf[written++] = (*start)[j];
+
+ *start += char_len;
+ }
+
+ return written;
}
/* TODO: could use dStr ADT! */
@@ -120,7 +140,7 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
DetectedContentType Type = DT_OCTET_STREAM; /* default to binary */
/* HTML try */
- for (i = 0; i < Size && isspace(p[i]); ++i);
+ for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrncasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrncasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrncasecmp(p+i, "<title", 6)) ||
@@ -150,7 +170,7 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
* All in the above set regard [00-31] as control characters.
* LATIN1: [7F-9F] unused
* CP-1251 {7F,98} unused (two characters).
- *
+ *
* We'll use [0-31] as indicators of non-text content.
* Better heuristics are welcomed! :-) */
@@ -158,19 +178,23 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
Size = MIN (Size, 256);
for (i = 0; i < Size; i++) {
int ch = (uchar_t) p[i];
- if (ch < 32 && !isspace(ch))
+ if (ch < 32 && !dIsspace(ch))
++bin_chars;
if (ch > 126)
++non_ascci;
if (ch > 190)
++non_ascci_text;
}
- if (bin_chars == 0) {
+ if (bin_chars == 0 && (non_ascci - non_ascci_text) <= Size/10) {
/* Let's say text: if "rare" chars are <= 10% */
- if ((non_ascci - non_ascci_text) <= Size/10)
+ Type = DT_TEXT_PLAIN;
+ } else if (Size > 0) {
+ /* a special check for UTF-8 */
+ Size = a_Utf8_end_of_char(p, Size - 1) + 1;
+ if (a_Utf8_test(p, Size) > 0)
Type = DT_TEXT_PLAIN;
}
- if (Size == 256)
+ if (Size >= 256)
st = 0;
}
@@ -180,11 +204,13 @@ int a_Misc_get_content_type_from_data(void *Data, size_t Size, const char **PT)
/*
* Parse Content-Type string, e.g., "text/html; charset=utf-8".
+ * Content-Type is defined in RFC 2045 section 5.1.
*/
-void a_Misc_parse_content_type(const char *str, char **major, char **minor,
+void a_Misc_parse_content_type(const char *type, char **major, char **minor,
char **charset)
{
- const char *s;
+ static const char tspecials_space[] = "()<>@,;:\\\"/[]?= ";
+ const char *str, *s;
if (major)
*major = NULL;
@@ -192,20 +218,31 @@ void a_Misc_parse_content_type(const char *str, char **major, char **minor,
*minor = NULL;
if (charset)
*charset = NULL;
- if (!str)
+ if (!(str = type))
return;
- for (s = str; isalnum(*s) || (*s == '-'); s++);
+ for (s = str; *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s);
+ s++) ;
if (major)
*major = dStrndup(str, s - str);
if (*s == '/') {
- for (str = ++s; isalnum(*s) || (*s == '-'); s++);
+ for (str = ++s;
+ *s && !iscntrl((uchar_t)*s) && !strchr(tspecials_space, *s); s++) ;
if (minor)
*minor = dStrndup(str, s - str);
}
-
- if (charset && *s) {
+ if (charset && *s &&
+ (dStrncasecmp(type, "text/", 5) == 0 ||
+ dStrncasecmp(type, "application/xhtml+xml", 21) == 0)) {
+ /* "charset" parameter defined for text media type in RFC 2046,
+ * application/xhtml+xml in RFC 3236.
+ *
+ * Note that RFC 3023 lists some main xml media types and provides
+ * the convention of using the "+xml" minor type suffix for other
+ * xml types, so it would be reasonable to check for that suffix if
+ * we have need to care about various xml types someday.
+ */
const char terminators[] = " ;\t";
const char key[] = "charset";
@@ -346,9 +383,9 @@ int a_Misc_parse_geometry(char *str, int *x, int *y, int *w, int *h)
*/
char *a_Misc_encode_base64(const char *in)
{
- static const char *base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
+ static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
char *out = NULL;
int len, i = 0;
diff --git a/src/misc.h b/src/misc.h
index d8de0238..0b4eaaa5 100644
--- a/src/misc.h
+++ b/src/misc.h
@@ -10,7 +10,7 @@ extern "C" {
char *a_Misc_escape_chars(const char *str, const char *esc_set);
-char *a_Misc_expand_tabs(const char *str, int len);
+int a_Misc_expand_tabs(char **start, char *end, char *buf, int buflen);
int a_Misc_get_content_type_from_data(void *Data, size_t Size,const char **PT);
int a_Misc_content_type_check(const char *EntryType, const char *DetectedType);
void a_Misc_parse_content_type(const char *str, char **major, char **minor,
diff --git a/src/msg.h b/src/msg.h
index 245ee803..c6ddcb48 100644
--- a/src/msg.h
+++ b/src/msg.h
@@ -11,28 +11,17 @@
#define _MSG_WARN(...)
#define _MSG_HTTP(...)
-
-#define MSG(...) \
+#define MSG_INNARDS(prefix, ...) \
D_STMT_START { \
if (prefs.show_msg){ \
- printf(__VA_ARGS__); \
+ printf(prefix __VA_ARGS__); \
fflush (stdout); \
} \
} D_STMT_END
-#define MSG_WARN(...) \
- D_STMT_START { \
- if (prefs.show_msg) \
- printf("** WARNING **: " __VA_ARGS__); \
- } D_STMT_END
-
-#define MSG_ERR(...) \
- D_STMT_START { \
- if (prefs.show_msg) \
- printf("** ERROR **: " __VA_ARGS__); \
- } D_STMT_END
-
-#define MSG_HTTP(...) \
- printf("HTTP warning: " __VA_ARGS__)
+#define MSG(...) MSG_INNARDS("", __VA_ARGS__)
+#define MSG_WARN(...) MSG_INNARDS("** WARNING **: ", __VA_ARGS__)
+#define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
+#define MSG_HTTP(...) MSG_INNARDS("HTTP warning: ", __VA_ARGS__)
#endif /* __MSG_H__ */
diff --git a/src/nav.c b/src/nav.c
index 2433d7b2..740bc565 100644
--- a/src/nav.c
+++ b/src/nav.c
@@ -163,7 +163,7 @@ static void Nav_save_scroll_pos(BrowserWindow *bw, int idx, int posx, int posy)
}
/*
- * Remove equal adyacent URLs at the top of the stack.
+ * Remove equal adjacent URLs at the top of the stack.
* (It may happen with redirections)
*/
static void Nav_stack_clean(BrowserWindow *bw)
@@ -191,25 +191,26 @@ static void Nav_stack_clean(BrowserWindow *bw)
* This function requests the page's root-URL; images and related stuff
* are fetched directly by the HTML module.
*/
-static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
+static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester, int offset)
{
- DilloUrl *old_url;
- bool_t MustLoad, ForceReload;
+ const DilloUrl *old_url;
+ bool_t MustLoad, ForceReload, Repush, IgnoreScroll;
int x, y, idx, ClientKey;
DilloWeb *Web;
MSG("Nav_open_url: new url='%s'\n", URL_STR_(url));
+ Repush = (URL_FLAGS(url) & URL_ReloadFromCache) != 0;
ForceReload = (URL_FLAGS(url) & (URL_E2EQuery + URL_ReloadFromCache)) != 0;
+ IgnoreScroll = (URL_FLAGS(url) & URL_IgnoreScroll) != 0;
/* Get the url of the current page */
idx = a_Nav_stack_ptr(bw);
old_url = a_History_get_url(NAV_UIDX(bw, idx));
_MSG("Nav_open_url: old_url='%s' idx=%d\n", URL_STR(old_url), idx);
/* Record current scrolling position */
- if (URL_FLAGS(url) & URL_ReloadFromCache) {
- /* Repush operation, don't change scroll position */
- } else if (old_url) {
+ if (old_url && !IgnoreScroll) {
a_UIcmd_get_scroll_xy(bw, &x, &y);
Nav_save_scroll_pos(bw, idx, x, y);
_MSG("Nav_open_url: saved scroll of '%s' at x=%d y=%d\n",
@@ -232,7 +233,7 @@ static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
// a_Menu_pagemarks_new(bw);
- Web = a_Web_new(url);
+ Web = a_Web_new(url, requester);
Web->bw = bw;
Web->flags |= WEB_RootUrl;
if ((ClientKey = a_Capi_open_url(Web, NULL, NULL)) != 0) {
@@ -249,12 +250,21 @@ static void Nav_open_url(BrowserWindow *bw, const DilloUrl *url, int offset)
void a_Nav_cancel_expect(BrowserWindow *bw)
{
if (bw->nav_expecting) {
- if (bw->nav_expect_url) {
- a_Url_free(bw->nav_expect_url);
- bw->nav_expect_url = NULL;
- }
+ a_Url_free(bw->nav_expect_url);
+ bw->nav_expect_url = NULL;
bw->nav_expecting = FALSE;
}
+ if (bw->meta_refresh_status > 0)
+ --bw->meta_refresh_status;
+}
+
+/*
+ * Cancel the expect if 'url' matches.
+ */
+void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url)
+{
+ if (bw->nav_expecting && a_Url_cmp(url, bw->nav_expect_url) == 0)
+ a_Nav_cancel_expect(bw);
}
/*
@@ -265,7 +275,7 @@ void a_Nav_cancel_expect(BrowserWindow *bw)
*/
void a_Nav_expect_done(BrowserWindow *bw)
{
- int url_idx, posx, posy, reload, repush, e2equery, goto_old_scroll = TRUE;
+ int m, url_idx, posx, posy, reload, repush, e2equery, goto_old_scroll=TRUE;
DilloUrl *url;
char *fragment = NULL;
@@ -278,11 +288,10 @@ void a_Nav_expect_done(BrowserWindow *bw)
e2equery = (URL_FLAGS(url) & URL_E2EQuery);
fragment = a_Url_decode_hex_str(URL_FRAGMENT_(url));
- /* Unset E2EQuery, ReloadPage and ReloadFromCache
+ /* Unset E2EQuery, ReloadPage, ReloadFromCache and IgnoreScroll
* before adding this url to history */
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_E2EQuery);
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_ReloadPage);
- a_Url_set_flags(url, URL_FLAGS(url) & ~URL_ReloadFromCache);
+ m = URL_E2EQuery|URL_ReloadPage|URL_ReloadFromCache|URL_IgnoreScroll;
+ a_Url_set_flags(url, URL_FLAGS(url) & ~m);
url_idx = a_History_add_url(url);
if (repush) {
@@ -310,7 +319,7 @@ void a_Nav_expect_done(BrowserWindow *bw)
}
if (goto_old_scroll) {
- /* Scroll to were we were in this page */
+ /* Scroll to where we were in this page */
Nav_get_scroll_pos(bw, &posx, &posy);
a_UIcmd_set_scroll_xy(bw, posx, posy);
_MSG("Nav: expect_done scrolling to x=%d y=%d\n", posx, posy);
@@ -333,7 +342,8 @@ void a_Nav_expect_done(BrowserWindow *bw)
* - Set bw to expect the URL data
* - Ask the cache to feed back the requested URL (via Nav_open_url)
*/
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
+void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester)
{
dReturn_if_fail (bw != NULL);
@@ -345,7 +355,7 @@ void a_Nav_push(BrowserWindow *bw, const DilloUrl *url)
a_Nav_cancel_expect(bw);
bw->nav_expect_url = a_Url_dup(url);
bw->nav_expecting = TRUE;
- Nav_open_url(bw, url, 0);
+ Nav_open_url(bw, url, requester, 0);
}
/*
@@ -362,13 +372,14 @@ static void Nav_repush(BrowserWindow *bw)
a_Url_set_flags(url, URL_FLAGS(url) | URL_ReloadFromCache);
bw->nav_expect_url = a_Url_dup(url);
bw->nav_expecting = TRUE;
- Nav_open_url(bw, url, 0);
+ Nav_open_url(bw, url, NULL, 0);
a_Url_free(url);
}
}
static void Nav_repush_callback(void *data)
{
+ _MSG(">>>> Nav_repush_callback <<<<\n");
Nav_repush(data);
a_Timeout_remove();
}
@@ -377,32 +388,50 @@ static void Nav_repush_callback(void *data)
* Repush current URL: not an end-to-end reload but from cache.
* - Currently used to switch to a charset decoder given by the META element.
* - Delayed to let dillo finish the call flow into a known state.
+ *
+ * There's no need to stop the parser before calling this function:
+ * When the timeout activates, a_Bw_stop_clients will stop the data feed.
*/
void a_Nav_repush(BrowserWindow *bw)
{
dReturn_if_fail (bw != NULL);
+ MSG(">>> a_Nav_repush <<<<\n");
a_Timeout_add(0.0, Nav_repush_callback, (void*)bw);
}
/*
- * Same as a_Nav_push() but in a new window.
+ * This one does a_Nav_redirection0's job.
*/
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url)
+static void Nav_redirection0_callback(void *data)
{
- int w, h;
- BrowserWindow *newbw;
+ BrowserWindow *bw = (BrowserWindow *)data;
+ const DilloUrl *referer_url = a_History_get_url(NAV_TOP_UIDX(bw));
+ _MSG(">>>> Nav_redirection0_callback <<<<\n");
- a_UIcmd_get_wh(bw, &w, &h);
- newbw = a_UIcmd_browser_window_new(w, h, bw);
- a_Nav_push(newbw, url);
+ if (bw->meta_refresh_status == 2) {
+ Nav_stack_move_ptr(bw, -1);
+ a_Nav_push(bw, bw->meta_refresh_url, referer_url);
+ }
+ a_Url_free(bw->meta_refresh_url);
+ bw->meta_refresh_url = NULL;
+ bw->meta_refresh_status = 0;
+ a_Timeout_remove();
}
/*
- * Wraps a_Nav_push to match 'DwPage->link' function type
+ * Handle a zero-delay URL redirection given by META
*/
-void a_Nav_vpush(void *vbw, const DilloUrl *url)
+void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url)
{
- a_Nav_push(vbw, url);
+ dReturn_if_fail (bw != NULL);
+ _MSG(">>> a_Nav_redirection0 <<<<\n");
+
+ a_Url_free(bw->meta_refresh_url);
+ bw->meta_refresh_url = a_Url_dup(new_url);
+ a_Url_set_flags(bw->meta_refresh_url,
+ URL_FLAGS(new_url)|URL_E2EQuery|URL_IgnoreScroll);
+ bw->meta_refresh_status = 2;
+ a_Timeout_add(0.0, Nav_redirection0_callback, (void*)bw);
}
/*
@@ -415,7 +444,7 @@ void a_Nav_back(BrowserWindow *bw)
a_Nav_cancel_expect(bw);
if (--idx >= 0){
a_UIcmd_set_msg(bw, "");
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), -1);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, -1);
}
}
@@ -429,7 +458,7 @@ void a_Nav_forw(BrowserWindow *bw)
a_Nav_cancel_expect(bw);
if (++idx < a_Nav_stack_size(bw)) {
a_UIcmd_set_msg(bw, "");
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), +1);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, +1);
}
}
@@ -438,56 +467,56 @@ void a_Nav_forw(BrowserWindow *bw)
*/
void a_Nav_home(BrowserWindow *bw)
{
- a_Nav_push(bw, prefs.home);
+ a_Nav_push(bw, prefs.home, NULL);
}
/*
* This one does a_Nav_reload's job!
*/
-static void Nav_reload(BrowserWindow *bw)
+static void Nav_reload_callback(void *data)
{
- DilloUrl *ReqURL;
+ BrowserWindow *bw = data;
+ const DilloUrl *h_url;
+ DilloUrl *r_url;
+ int choice, confirmed = 1;
a_Nav_cancel_expect(bw);
if (a_Nav_stack_size(bw)) {
- ReqURL = a_Url_dup(a_History_get_url(NAV_TOP_UIDX(bw)));
- /* Mark URL as reload to differentiate from push */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_ReloadPage);
- /* Let's make reload be end-to-end */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) | URL_E2EQuery);
- /* This is an explicit reload, so clear the SpamSafe flag */
- a_Url_set_flags(ReqURL, URL_FLAGS(ReqURL) & ~URL_SpamSafe);
- bw->nav_expect_url = ReqURL;
- bw->nav_expecting = TRUE;
- Nav_open_url(bw, ReqURL, 0);
+ h_url = a_History_get_url(NAV_TOP_UIDX(bw));
+ if (strncmp(URL_STR(h_url), "dpi:/vsource/", 13) == 0) {
+ /* disable reload for view source dpi */
+ confirmed = 0;
+ } else if (URL_FLAGS(h_url) & URL_Post) {
+ /* Attempt to repost data, let's confirm... */
+ choice = a_Dialog_choice3("Repost form data?",
+ "Yes", "*No", "Cancel");
+ confirmed = (choice == 0); /* "Yes" */
+ }
+
+ if (confirmed) {
+ r_url = a_Url_dup(h_url);
+ /* Mark URL as reload to differentiate from push */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_ReloadPage);
+ /* Let's make reload be end-to-end */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) | URL_E2EQuery);
+ /* This is an explicit reload, so clear the SpamSafe flag */
+ a_Url_set_flags(r_url, URL_FLAGS(r_url) & ~URL_SpamSafe);
+ bw->nav_expect_url = r_url;
+ bw->nav_expecting = TRUE;
+ Nav_open_url(bw, r_url, NULL, 0);
+ }
}
}
/*
* Implement the RELOAD button functionality.
* (Currently it only reloads the page, not its images)
+ * Note: the timeout lets CCC operations end before making the request.
*/
void a_Nav_reload(BrowserWindow *bw)
{
- DilloUrl *url;
- int choice;
-
- a_Nav_cancel_expect(bw);
- if (a_Nav_stack_size(bw)) {
- url = a_History_get_url(NAV_TOP_UIDX(bw));
- if (URL_FLAGS(url) & URL_Post) {
- /* Attempt to repost data, let's confirm... */
- choice = a_Dialog_choice3("Repost form data?",
- "Yes", "*No", "Cancel");
- if (choice == 0) { /* "Yes" */
- _MSG("Nav_reload_confirmed\n");
- Nav_reload(bw);
- }
-
- } else {
- Nav_reload(bw);
- }
- }
+ dReturn_if_fail (bw != NULL);
+ a_Timeout_add(0.0, Nav_reload_callback, (void*)bw);
}
/*
@@ -498,10 +527,10 @@ void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw)
int idx = a_Nav_stack_ptr(bw) + offset;
if (new_bw) {
- a_Nav_push_nw(bw, a_History_get_url(NAV_UIDX(bw,idx)));
+ a_UIcmd_open_url_nw(bw, a_History_get_url(NAV_UIDX(bw,idx)));
} else {
a_Nav_cancel_expect(bw);
- Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), offset);
+ Nav_open_url(bw, a_History_get_url(NAV_UIDX(bw,idx)), NULL, offset);
a_UIcmd_set_buttons_sens(bw);
}
}
@@ -538,9 +567,9 @@ static void Nav_save_cb(int Op, CacheClient_t *Client)
void a_Nav_save_url(BrowserWindow *bw,
const DilloUrl *url, const char *filename)
{
- DilloWeb *Web = a_Web_new(url);
+ DilloWeb *Web = a_Web_new(url, NULL);
Web->bw = bw;
- Web->filename = dStrdup(filename);
+ Web->filename = dStrdup(filename);
Web->flags |= WEB_Download;
/* TODO: keep track of this client */
a_Capi_open_url(Web, Nav_save_cb, Web);
@@ -559,5 +588,13 @@ int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize)
*/
void a_Nav_unref_buf(const DilloUrl *Url)
{
- return a_Capi_unref_buf(Url);
+ a_Capi_unref_buf(Url);
+}
+
+/*
+ * Wrapper for a_Capi_set_vsource_url().
+ */
+void a_Nav_set_vsource_url(const DilloUrl *Url)
+{
+ a_Capi_set_vsource_url(Url);
}
diff --git a/src/nav.h b/src/nav.h
index 4a03960a..ad1270d7 100644
--- a/src/nav.h
+++ b/src/nav.h
@@ -13,9 +13,9 @@
extern "C" {
#endif /* __cplusplus */
-void a_Nav_push(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_push_nw(BrowserWindow *bw, const DilloUrl *url);
-void a_Nav_vpush(void *vbw, const DilloUrl *url);
+void a_Nav_redirection0(BrowserWindow *bw, const DilloUrl *new_url);
+void a_Nav_push(BrowserWindow *bw, const DilloUrl *url,
+ const DilloUrl *requester);
void a_Nav_repush(BrowserWindow *bw);
void a_Nav_back(BrowserWindow *bw);
void a_Nav_forw(BrowserWindow *bw);
@@ -24,6 +24,7 @@ void a_Nav_reload(BrowserWindow *bw);
void a_Nav_jump(BrowserWindow *bw, int offset, int new_bw);
void a_Nav_free(BrowserWindow *bw);
void a_Nav_cancel_expect (BrowserWindow *bw);
+void a_Nav_cancel_expect_if_eq(BrowserWindow *bw, const DilloUrl *url);
void a_Nav_expect_done(BrowserWindow *bw);
int a_Nav_stack_ptr(BrowserWindow *bw);
int a_Nav_stack_size(BrowserWindow *bw);
@@ -34,6 +35,7 @@ void a_Nav_save_url(BrowserWindow *bw,
const DilloUrl *url, const char *filename);
int a_Nav_get_buf(const DilloUrl *Url, char **PBuf, int *BufSize);
void a_Nav_unref_buf(const DilloUrl *Url);
+void a_Nav_set_vsource_url(const DilloUrl *Url);
#ifdef __cplusplus
}
diff --git a/src/paths.cc b/src/paths.cc
new file mode 100644
index 00000000..6fccb89a
--- /dev/null
+++ b/src/paths.cc
@@ -0,0 +1,101 @@
+/*
+ * File: paths.cc
+ *
+ * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "msg.h"
+#include "../dlib/dlib.h"
+#include "paths.hh"
+
+/*
+ * Local data
+ */
+
+// Dillo works from an unmounted directory (/tmp)
+static char* oldWorkingDir = NULL;
+
+/*
+ * Changes current working directory to /tmp and creates ~/.dillo
+ * if not exists.
+ */
+void Paths::init(void)
+{
+ char *path;
+ struct stat st;
+ int rc = 0;
+
+ dFree(oldWorkingDir);
+ oldWorkingDir = dGetcwd();
+ rc = chdir("/tmp");
+ if (rc == -1) {
+ MSG("paths: error changing directory to /tmp: %s\n",
+ dStrerror(errno));
+ }
+
+ path = dStrconcat(dGethomedir(), "/.dillo", NULL);
+ if (stat(path, &st) == -1) {
+ if (errno == ENOENT) {
+ MSG("paths: creating directory %s.\n", path);
+ if (mkdir(path, 0700) < 0) {
+ MSG("paths: error creating directory %s: %s\n",
+ path, dStrerror(errno));
+ }
+ } else {
+ MSG("Dillo: error reading %s: %s\n", path, dStrerror(errno));
+ }
+ }
+
+ dFree(path);
+}
+
+/*
+ * Return the initial current working directory in a string.
+ */
+char *Paths::getOldWorkingDir(void)
+{
+ return oldWorkingDir;
+}
+
+/*
+ * Free memory
+ */
+void Paths::free(void)
+{
+ dFree(oldWorkingDir);
+}
+
+/*
+ * Examines the path for "rcFile" and assign its file pointer to "fp".
+ */
+FILE *Paths::getPrefsFP(const char *rcFile)
+{
+ FILE *fp;
+ char *path = dStrconcat(dGethomedir(), "/.dillo/", rcFile, NULL);
+
+ if (!(fp = fopen(path, "r"))) {
+ MSG("paths: Cannot open file '%s'\n", path);
+
+ char *path2 = dStrconcat(DILLO_SYSCONF, rcFile, NULL);
+ if (!(fp = fopen(path2, "r"))) {
+ MSG("paths: Cannot open file '%s'\n",path2);
+ MSG("paths: Using internal defaults...\n");
+ } else {
+ MSG("paths: Using %s\n", path2);
+ }
+ dFree(path2);
+ }
+
+ dFree(path);
+ return fp;
+}
+
diff --git a/src/paths.hh b/src/paths.hh
new file mode 100644
index 00000000..a9efc62b
--- /dev/null
+++ b/src/paths.hh
@@ -0,0 +1,26 @@
+/*
+ * File: paths.hh
+ *
+ * Copyright 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PATHS_HH__
+#define __PATHS_HH__
+
+#define PATHS_RC_PREFS "dillorc"
+#define PATHS_RC_KEYS "keysrc"
+
+class Paths {
+public:
+ static void init(void);
+ static void free(void);
+ static char *getOldWorkingDir(void);
+ static FILE *getPrefsFP(const char *rcFile);
+};
+
+#endif /* __PATHS_HH__ */
diff --git a/src/pixmaps.h b/src/pixmaps.h
index 4e54ff71..83235dd2 100644
--- a/src/pixmaps.h
+++ b/src/pixmaps.h
@@ -6,7 +6,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
@@ -675,6 +675,7 @@ static const char *const stop_xpm[] = {
" .KLLLLLLLK. ",
" ......... ",
" "};
+
/* XPM */
static const char *const bm_xpm[] = {
"22 22 86 1",
@@ -787,6 +788,57 @@ static const char *const bm_xpm[] = {
" .S. TU. ",
" . . "};
+/* XPM */
+static const char *const tools_xpm[] = {
+"22 22 25 1",
+" c #None",
+". c #696977777676",
+"X c #96969B9B9B9B",
+"o c #848490908F8F",
+"O c #D7D7D7D7D7D7",
+"+ c #B5B5B7B7B7B7",
+"@ c #EAEAEAEAEAEA",
+"# c #797987878787",
+"$ c #F1F1EFEFEFEF",
+"% c #CCCCCCCCCCCC",
+"& c #8B8B97979696",
+"* c #CACACACACACA",
+"= c #787878787878",
+"- c #AAAAB5B5B5B5",
+"; c #F9F9F8F8F9F9",
+": c #A6A6A8A8A8A8",
+"> c #7F7F90907F7F",
+", c #A4A4BDBDA4A4",
+"< c #A5A58A8A5353",
+"1 c #00008B8B0000",
+"2 c #6C6C6C6C6C6C",
+"3 c #BCBCC3C3C2C2",
+"4 c #CACAD8D8D8D8",
+"5 c #9999A8A8A8A8",
+"6 c #848489898989",
+" ",
+" ",
+" .XXo. ",
+" oO $$+. ",
+" oo@$$$@# ",
+" #o@$$$O# ",
+" #X$$$$%X ",
+" &#% #X$$$@o ",
+" #*o =-@$$$# ",
+" oO*o #$@$$$$# ",
+" oOOOX oO@;$$$$X ",
+" #@O*OXo:>,<$$$$o ",
+" XO@O*O*12>,<$$$# ",
+" X$@O*+<12>,<$$& ",
+" +$@O*+<12>,<;@ ",
+" :$@O*+<12>,<$@ ",
+" &34O*-<11>,<$$ ",
+" -X&X&<12>,<$O ",
+" 45<11>,<3 ",
+" 45<1=>O# ",
+" -&X6&=# ",
+" "};
+
/* Small icons here */
/* XPM */
@@ -1321,6 +1373,73 @@ static const char *const bm_s_xpm[] = {
" .v. wx. "};
/* XPM */
+static const char *const tools_s_xpm[] = {
+"16 16 45 1",
+" c #7B127D1C7D08",
+". c #82778EED8E15",
+"X c #8F3F975396FF",
+"o c #9A98A40CA38F",
+"O c #833D8EC28E02",
+"+ c #99C89EDF9EC2",
+"@ c #B347BD91BD42",
+"# c #7E4181A081AF",
+"$ c #8744915990DA",
+"% c #86728A968A7C",
+"& c #8D56924B9226",
+"* c #96DF9AC79AA6",
+"= c #9E01A473A438",
+"- c #AB41B55EB546",
+"; c #ABD1B155B117",
+": c #B5BFBC3DBC2F",
+"> c #B917BCECBCAB",
+", c #B558B98BB958",
+"< c #CEA2CFDBCFD4",
+"1 c #A699AC55ABF1",
+"2 c #C2A5C5A8C57A",
+"3 c #C8B2CA27CA1C",
+"4 c #D80FD952D955",
+"5 c #DF19E120E134",
+"6 c #EAC7E9ECE9FE",
+"7 c #F164EF95EF9D",
+"8 c #F6E0F5E6F61C",
+"9 c #F013EEAEEEBF",
+"0 c #487651675130",
+"q c #6E4D7BA97B06",
+"w c #88F190679051",
+"e c #92339C0E9BC1",
+"r c #A1BBAB0FAA98",
+"t c #A24AA7C4A7A7",
+"y c #86C7935E92DC",
+"u c #C8C4D721D725",
+"i c #D0E6D516D518",
+"p c #76A38450834A",
+"a c #6B4B7B507A6D",
+"s c #7C6D882B878D",
+"d c #88F395099487",
+"f c #DB34DCE9DCF5",
+"g c #5B05673F66CD",
+"h c #9402A0549FCC",
+" c None",
+/* pixels */
+" ",
+" gO+Oq ",
+" p*45<Xg ",
+" h.>885oa ",
+" p.3883Xg ",
+" i$O s$488;s ",
+" O>1p e;588;a ",
+" O331sr;7778:s ",
+" O343,**3678;a ",
+" f,643;#%368:s ",
+" t<64>= &374f ",
+" t244>= &379ih",
+" d@:;=& &475u",
+" ssr-* *44r",
+" u-%#*%0",
+" feww0g"
+};
+
+/* XPM */
static const char *const new_s_xpm[] = {
"11 11 35 1",
" c None",
@@ -1402,6 +1521,42 @@ static const char *const search_xpm[] = {
" 11 "};
/* XPM */
+static const char *const help_xpm[] = {
+"14 16 16 1",
+" c None",
+"1 c #DBDBDB",
+"2 c #B6B6B6",
+"3 c #929292",
+"4 c #6D6D6D",
+"5 c #F1EFEF",
+"6 c #018B00",
+"7 c #A48A53",
+"8 c #A4BDA4",
+"9 c #000000",
+"A c #000000",
+"B c #000000",
+"C c #000000",
+"D c #000000",
+"E c #000000",
+"F c #000000",
+" 5555555 ",
+" 544433355 ",
+" 54555438455 ",
+" 44555542835 ",
+" 44445548245 ",
+" 4444554274 ",
+" 544554274 ",
+" 55474 ",
+" 5474 ",
+" 544 ",
+" 44 ",
+" ",
+" 5445 ",
+" 4764 ",
+" 4674 ",
+" 5445 "};
+
+/* XPM */
static const char *const full_screen_on_xpm[] = {
"13 15 2 1",
" c None",
@@ -1496,48 +1651,6 @@ static const char *const mini_ok_xpm[] = {
};
/* XPM */
-static const char *const imgload_on_xpm[] = {
-"15 15 2 1",
-" c None",
-". c #00000000CF3C",
-" ",
-" . . . ... ",
-" . .. .. . . ",
-" . . . . . ",
-" . . . . .. ",
-" . . . . . ",
-" . . . ... ",
-" ",
-" ... . . ",
-" . . .. . ",
-" . . . . . ",
-" . . . .. ",
-" . . . . ",
-" ... . . ",
-" "};
-
-/* XPM */
-static const char *const imgload_off_xpm[] = {
-"15 15 2 1",
-" c None",
-". c #CF3C00000000",
-" ",
-" . . . ... ",
-" . .. .. . . ",
-" . . . . . ",
-" . . . . .. ",
-" . . . . . ",
-" . . . ... ",
-" ",
-" ... ... ... ",
-" . . . . ",
-" . . ... ... ",
-" . . . . ",
-" . . . . ",
-" ... . . ",
-" "};
-
-/* XPM */
static const char *const left_i_xpm[] = {
"22 22 3 1",
" c None",
diff --git a/src/plain.cc b/src/plain.cc
index b75dbea5..4da618e4 100644
--- a/src/plain.cc
+++ b/src/plain.cc
@@ -13,15 +13,13 @@
* Module for decoding a text/plain object into a dw widget.
*/
-#include <string.h> /* for memcpy and memmove */
-#include <math.h> /* for rint() */
-
#include "msg.h"
#include "prefs.h"
#include "cache.h"
#include "bw.h"
#include "web.hh"
#include "misc.h"
+#include "styleengine.hh"
#include "uicmd.hh"
@@ -37,23 +35,25 @@ using namespace dw::core;
class DilloPlain {
private:
- class PlainEventReceiver: public dw::core::Widget::EventReceiver {
+ class PlainLinkReceiver: public dw::core::Layout::LinkReceiver {
public:
DilloPlain *plain;
- bool buttonPress(dw::core::Widget *widget, dw::core::EventButton *event);
+ bool press(dw::core::Widget *widget, int link, int img, int x, int y,
+ dw::core::EventButton *event);
};
- PlainEventReceiver plainReceiver;
+ PlainLinkReceiver plainReceiver;
+
+ void addLine(char *Buf, uint_t BufSize);
public:
BrowserWindow *bw;
- DilloUrl *url;
Widget *dw;
style::Style *widgetStyle;
size_t Start_Ofs; /* Offset of where to start reading next */
int state;
- DilloPlain(BrowserWindow *bw, const DilloUrl *url);
+ DilloPlain(BrowserWindow *bw);
~DilloPlain();
void write(void *Buf, uint_t BufSize, int Eof);
@@ -83,38 +83,27 @@ void a_Plain_free(void *data);
/*
* Diplain constructor.
*/
-DilloPlain::DilloPlain(BrowserWindow *p_bw, const DilloUrl *p_url)
+DilloPlain::DilloPlain(BrowserWindow *p_bw)
{
- style::StyleAttrs styleAttrs;
- style::FontAttrs fontAttrs;
-
/* Init event receiver */
plainReceiver.plain = this;
/* Init internal variables */
bw = p_bw;
- url = a_Url_dup(p_url);
dw = new Textblock (prefs.limit_text_width);
Start_Ofs = 0;
state = ST_SeekingEol;
- /* Create the font and attribute for the page. */
- fontAttrs.name = prefs.fw_fontname;
- fontAttrs.size = (int) rint(14.0 * prefs.font_factor);
- fontAttrs.weight = 400;
- fontAttrs.style = style::FONT_STYLE_NORMAL;
-
- Layout *layout = (Layout*)bw->render_layout;
- styleAttrs.initValues ();
- styleAttrs.margin.setVal (5);
- styleAttrs.font = style::Font::create (layout, &fontAttrs);
- styleAttrs.color = style::Color::createSimple (layout, prefs.text_color);
- styleAttrs.backgroundColor =
- style::Color::createSimple (layout, prefs.bg_color);
- widgetStyle = style::Style::create (layout, &styleAttrs);
+ Layout *layout = (Layout*) bw->render_layout;
+ StyleEngine styleEngine (layout);
+
+ styleEngine.startElement ("body");
+ styleEngine.startElement ("pre");
+ widgetStyle = styleEngine.wordStyle ();
+ widgetStyle->ref ();
/* The context menu */
- DW2TB(dw)->connectEvent (&plainReceiver);
+ layout->connectLink (&plainReceiver);
/* Hook destructor to the dw delete call */
dw->setDeleteCallback(a_Plain_free, this);
@@ -125,26 +114,44 @@ DilloPlain::DilloPlain(BrowserWindow *p_bw, const DilloUrl *p_url)
*/
DilloPlain::~DilloPlain()
{
- MSG("::~DilloPlain()\n");
- a_Url_free(url);
+ _MSG("::~DilloPlain()\n");
widgetStyle->unref();
}
/*
* Receive the mouse button press event
*/
-bool DilloPlain::PlainEventReceiver::buttonPress (Widget *widget,
- EventButton *event)
+bool DilloPlain::PlainLinkReceiver::press (Widget *widget, int, int, int, int,
+ EventButton *event)
{
- _MSG("DilloPlain::PlainEventReceiver::buttonPress\n");
+ _MSG("DilloPlain::PlainLinkReceiver::buttonPress\n");
if (event->button == 3) {
- a_UIcmd_page_popup(plain->bw, plain->url, FALSE, FALSE);
+ a_UIcmd_page_popup(plain->bw, FALSE, NULL);
return true;
}
return false;
}
+void DilloPlain::addLine(char *Buf, uint_t BufSize)
+{
+ int len;
+ char buf[128];
+ char *end = Buf + BufSize;
+
+ if (BufSize > 0) {
+ // Limit word length to avoid X11 coordinate
+ // overflow with extremely long lines.
+ while ((len = a_Misc_expand_tabs(&Buf, end, buf, sizeof(buf))))
+ DW2TB(dw)->addText(buf, len, widgetStyle);
+ } else {
+ // Add dummy word for empty lines - otherwise the parbreak is ignored.
+ DW2TB(dw)->addText("", 0, widgetStyle);
+ }
+
+ DW2TB(dw)->addParbreak(0, widgetStyle);
+}
+
/*
* Here we parse plain text and put it into the page structure.
* (This function is called by Plain_callback whenever there's new data)
@@ -152,7 +159,6 @@ bool DilloPlain::PlainEventReceiver::buttonPress (Widget *widget,
void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
{
char *Start;
- char *data;
uint_t i, len, MaxBytes;
_MSG("DilloPlain::write Eof=%d\n", Eof);
@@ -170,10 +176,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
}
break;
case ST_Eol:
- data = a_Misc_expand_tabs(Start + i - len, len);
- DW2TB(dw)->addText(data, widgetStyle);
- DW2TB(dw)->addParbreak(0, widgetStyle);
- dFree(data);
+ addLine(Start + i - len, len);
if (Start[i] == '\r' && Start[i + 1] == '\n') ++i;
if (i < MaxBytes) ++i;
state = ST_SeekingEol;
@@ -183,10 +186,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
}
Start_Ofs += i - len;
if (Eof && len) {
- data = a_Misc_expand_tabs(Start + i - len, len);
- DW2TB(dw)->addText(data, widgetStyle);
- DW2TB(dw)->addParbreak(0, widgetStyle);
- dFree(data);
+ addLine(Start + i - len, len);
Start_Ofs += len;
}
@@ -199,7 +199,7 @@ void DilloPlain::write(void *Buf, uint_t BufSize, int Eof)
void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call, void **Data)
{
DilloWeb *web = (DilloWeb*)P;
- DilloPlain *plain = new DilloPlain(web->bw, web->url);
+ DilloPlain *plain = new DilloPlain(web->bw);
*Call = (CA_Callback_t)Plain_callback;
*Data = (void*)plain;
@@ -209,7 +209,7 @@ void *a_Plain_text(const char *type, void *P, CA_Callback_t *Call, void **Data)
void a_Plain_free(void *data)
{
- MSG("a_Plain_free! %p\n", data);
+ _MSG("a_Plain_free! %p\n", data);
delete ((DilloPlain *)data);
}
diff --git a/src/png.c b/src/png.c
index fa07e781..15f26e3b 100644
--- a/src/png.c
+++ b/src/png.c
@@ -4,6 +4,7 @@
*
* Geoff Lane nov 1999 zzassgl@twirl.mcc.ac.uk
* Luca Rota, Jorge Arellano Cid, Eric Gaudet 2000
+ * Jorge Arellano Cid 2009
*
* "PNG: The Definitive Guide" by Greg Roelofs, O'Reilly
* ISBN 1-56592-542-4
@@ -12,12 +13,8 @@
#include <config.h>
#ifdef ENABLE_PNG
-#include <stdio.h>
-#include <string.h>
#include <stdlib.h> /* For abort() */
-#include <zlib.h>
-
#ifdef HAVE_LIBPNG_PNG_H
#include <libpng/png.h>
#else
@@ -26,10 +23,8 @@
#include "msg.h"
#include "image.hh"
-#include "web.hh"
#include "cache.h"
#include "dicache.h"
-#include "prefs.h"
enum prog_state {
IS_finished, IS_init, IS_nextdata
@@ -58,7 +53,7 @@ static char *prog_state_name[] =
* structure below so that processing can be suspended or resumed at any
* point within an input image.
*
- * In the case of the libpng library, it maintains it's own state in
+ * In the case of the libpng library, it maintains its own state in
* png_ptr and into_ptr so the FSM is very simple - much simpler than the
* ones for XBM and PNM are.
*/
@@ -67,19 +62,19 @@ typedef
struct _DilloPng {
DilloImage *Image; /* Image meta data */
DilloUrl *url; /* Primary Key for the dicache */
- int version; /* Secondary Key for the dicache */
+ int version; /* Secondary Key for the dicache */
double display_exponent; /* gamma correction */
- ulong_t width; /* png image width */
- ulong_t height; /* png image height */
+ ulong_t width; /* png image width */
+ ulong_t height; /* png image height */
png_structp png_ptr; /* libpng private data */
png_infop info_ptr; /* libpng private info */
- uchar_t *image_data; /* decoded image data */
- uchar_t **row_pointers; /* pntr to row starts */
+ uchar_t *image_data; /* decoded image data */
+ uchar_t **row_pointers; /* pntr to row starts */
jmp_buf jmpbuf; /* png error processing */
- int error; /* error flag */
+ int error; /* error flag */
png_uint_32 previous_row;
- int rowbytes; /* No. bytes in image row */
+ int rowbytes; /* No. bytes in image row */
short passes;
short channels; /* No. image channels */
@@ -92,13 +87,13 @@ struct _DilloPng {
* ipbuf ipbufstart ipbufsize
*/
- uchar_t *ipbuf; /* image data in buffer */
- int ipbufstart; /* first valid image byte */
- int ipbufsize; /* size of valid data in */
+ uchar_t *ipbuf; /* image data in buffer */
+ int ipbufstart; /* first valid image byte */
+ int ipbufsize; /* size of valid data in */
enum prog_state state; /* FSM current state */
- uchar_t *linebuf; /* o/p raster data */
+ uchar_t *linebuf; /* o/p raster data */
} DilloPng;
@@ -106,13 +101,6 @@ struct _DilloPng {
#define BLACK 0
#define WHITE 255
-/*
- * Forward declarations
- */
-/* exported function */
-void *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data);
-
static
void Png_error_handling(png_structp png_ptr, png_const_charp msg)
@@ -146,6 +134,15 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
&bit_depth, &color_type, &interlace_type, NULL, NULL);
+ /* check max image size */
+ if (png->width <= 0 || png->height <= 0 ||
+ png->width > IMAGE_MAX_AREA / png->height) {
+ MSG("Png_datainfo_callback: suspicious image size request %ldx%ld\n",
+ png->width, png->height);
+ Png_error_handling(png_ptr, "Aborting...");
+ return; /* not reached */
+ }
+
_MSG("Png_datainfo_callback: png->width = %ld\n"
"Png_datainfo_callback: png->height = %ld\n",
png->width, png->height);
@@ -185,7 +182,7 @@ Png_datainfo_callback(png_structp png_ptr, png_infop info_ptr)
png->passes = png_set_interlace_handling(png_ptr);
}
- /* get libpng to update it's state */
+ /* get libpng to update its state */
png_read_update_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &png->width, &png->height,
@@ -230,14 +227,15 @@ static void
png_progressive_combine_row(png_ptr, png->row_pointers[row_num], new_row);
+ _MSG("png: row_num=%u previous_row=%u\n", row_num, png->previous_row);
if (row_num < png->previous_row) {
- a_Dicache_new_scan(png->Image, png->url, png->version);
+ a_Dicache_new_scan(png->url, png->version);
}
png->previous_row = row_num;
switch (png->channels) {
case 3:
- a_Dicache_write(png->Image, png->url, png->version,
+ a_Dicache_write(png->url, png->version,
png->image_data + (row_num * png->rowbytes),
(uint_t)row_num);
break;
@@ -275,79 +273,67 @@ static void
data++;
}
}
- a_Dicache_write(png->Image, png->url, png->version,
- png->linebuf, (uint_t)row_num);
+ a_Dicache_write(png->url, png->version, png->linebuf, (uint_t)row_num);
break;
}
default:
- MSG("Png_datarow_callback: unexpected number of channels = %d\n",
- png->channels);
+ MSG("Png_datarow_callback: unexpected number of channels=%d pass=%d\n",
+ png->channels, pass);
abort();
}
}
-static void
- Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
+static void Png_dataend_callback(png_structp png_ptr, png_infop info_ptr)
{
DilloPng *png;
_MSG("Png_dataend_callback:\n");
+ if (!info_ptr)
+ MSG("Png_dataend_callback: info_ptr = NULL\n");
png = png_get_progressive_ptr(png_ptr);
png->state = IS_finished;
}
-
/*
- * Op: Operation to perform.
- * If (Op == 0)
- * start or continue processing an image if image data exists.
- * else
- * terminate processing, cleanup any allocated memory,
- * close down the decoding process.
- *
- * Client->CbData : pointer to previously allocated DilloPng work area.
- * This holds the current state of the image processing and is saved
- * across calls to this routine.
- * Client->Buf : Pointer to data start.
- * Client->BufSize : the size of the data buffer.
- *
- * You have to keep track of where you are in the image data and
- * how much has been processed.
- *
- * It's entirely possible that you will not see the end of the data. The
- * user may terminate transfer via a Stop button or there may be a network
- * failure. This means that you can't just wait for all the data to be
- * presented before starting conversion and display.
+ * Free up the resources for this image.
*/
-static void Png_callback(int Op, CacheClient_t *Client)
+static void Png_free(DilloPng *png)
{
- DilloPng *png = Client->CbData;
-
- if (Op) {
- /* finished - free up the resources for this image */
- a_Dicache_close(png->url, png->version, Client);
- dFree(png->image_data);
- dFree(png->row_pointers);
- dFree(png->linebuf);
-
- if (setjmp(png->jmpbuf))
- MSG_WARN("PNG: can't destroy read structure\n");
- else if (png->png_ptr)
- png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
- dFree(png);
- return;
- }
+ _MSG("Png_free: png=%p\n", png);
+
+ dFree(png->image_data);
+ dFree(png->row_pointers);
+ dFree(png->linebuf);
+ if (setjmp(png->jmpbuf))
+ MSG_WARN("PNG: can't destroy read structure\n");
+ else if (png->png_ptr)
+ png_destroy_read_struct(&png->png_ptr, &png->info_ptr, NULL);
+ dFree(png);
+}
- /* Let's make some sound if we have been called with no data */
- dReturn_if_fail ( Client->Buf != NULL && Client->BufSize > 0 );
+/*
+ * Finish the decoding process (and free the memory)
+ */
+static void Png_close(DilloPng *png, CacheClient_t *Client)
+{
+ _MSG("Png_close\n");
+ /* Let dicache know decoding is over */
+ a_Dicache_close(png->url, png->version, Client);
+ Png_free(png);
+}
- _MSG("Png_callback BufSize = %d\n", Client->BufSize);
+/*
+ * Receive and process new chunks of PNG image data
+ */
+static void Png_write(DilloPng *png, void *Buf, uint_t BufSize)
+{
+ dReturn_if_fail ( Buf != NULL && BufSize > 0 );
/* Keep local copies so we don't have to pass multiple args to
* a number of functions. */
- png->ipbuf = Client->Buf;
- png->ipbufsize = Client->BufSize;
+ png->ipbuf = Buf;
+ png->ipbufsize = BufSize;
/* start/resume the FSM here */
while (png->state != IS_finished && DATASIZE) {
@@ -359,8 +345,7 @@ static void Png_callback(int Op, CacheClient_t *Client)
return; /* need MORE data */
}
/* check the image signature - DON'T update ipbufstart! */
- if (!png_check_sig(png->ipbuf, DATASIZE)) {
- /* you lied to me about it being a PNG image */
+ if (png_sig_cmp(png->ipbuf, 0, DATASIZE)) {
MSG_WARN("\"%s\" is not a PNG file.\n", URL_STR(png->url));
png->state = IS_finished;
break;
@@ -408,11 +393,47 @@ static void Png_callback(int Op, CacheClient_t *Client)
}
/*
+ * Op: Operation to perform.
+ * If (Op == 0)
+ * start or continue processing an image if image data exists.
+ * else
+ * terminate processing, cleanup any allocated memory,
+ * close down the decoding process.
+ *
+ * Client->CbData : pointer to previously allocated DilloPng work area.
+ * This holds the current state of the image processing and is kept
+ * across calls to this routine.
+ * Client->Buf : Pointer to data start.
+ * Client->BufSize : the size of the data buffer.
+ *
+ * You have to keep track of where you are in the image data and
+ * how much has been processed.
+ *
+ * It's entirely possible that you will not see the end of the data. The
+ * user may terminate transfer via a Stop button or there may be a network
+ * failure. This means that you can't just wait for all the data to be
+ * presented before starting conversion and display.
+ */
+void a_Png_callback(int Op, void *data)
+{
+ if (Op == CA_Send) {
+ CacheClient_t *Client = data;
+ Png_write(Client->CbData, Client->Buf, Client->BufSize);
+ } else if (Op == CA_Close) {
+ CacheClient_t *Client = data;
+ Png_close(Client->CbData, Client);
+ } else if (Op == CA_Abort) {
+ Png_free(data);
+ }
+}
+
+/*
* Create the image state data that must be kept between calls
*/
-static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, int version)
+void *a_Png_new(DilloImage *Image, DilloUrl *url, int version)
{
DilloPng *png = dNew0(DilloPng, 1);
+ _MSG("a_Png_new: png=%p\n", png);
png->Image = Image;
png->url = url;
@@ -430,49 +451,9 @@ static DilloPng *Png_new(DilloImage *Image, DilloUrl *url, int version)
return png;
}
-/*
- * MIME handler for "image/png" type
- * (Sets Png_callback or a_Dicache_callback as the cache-client)
- */
-void *a_Png_image(const char *Type, void *Ptr, CA_Callback_t *Call,
- void **Data)
-{
-/*
- * Type: MIME type
- * Ptr: points to a Web structure
- * Call: Dillo calls this with more data/eod
- * Data: raw image data
- */
+#else /* ENABLE_PNG */
- DilloWeb *web = Ptr;
- DICacheEntry *DicEntry;
-
- _MSG("a_Png_image: Type = %s\n"
- "a_Png_image: libpng - Compiled %s; using %s.\n"
- "a_Png_image: zlib - Compiled %s; using %s.\n",
- Type, PNG_LIBPNG_VER_STRING, png_libpng_ver,ZLIB_VERSION,zlib_version);
-
- if (!web->Image)
- web->Image = a_Image_new(0, 0, NULL, prefs.bg_color);
-
- /* Add an extra reference to the Image (for dicache usage) */
- a_Image_ref(web->Image);
-
- DicEntry = a_Dicache_get_entry(web->url);
- if (!DicEntry) {
- /* Let's create an entry for this image... */
- DicEntry = a_Dicache_add_entry(web->url);
-
- /* ... and let the decoder feed it! */
- *Data = Png_new(web->Image, DicEntry->url, DicEntry->version);
- *Call = (CA_Callback_t) Png_callback;
- } else {
- /* Let's feed our client from the dicache */
- a_Dicache_ref(DicEntry->url, DicEntry->version);
- *Data = web->Image;
- *Call = (CA_Callback_t) a_Dicache_callback;
- }
- return (web->Image->dw);
-}
+void *a_Png_new() { return 0; }
+void a_Png_callback() { return; }
#endif /* ENABLE_PNG */
diff --git a/src/prefs.c b/src/prefs.c
index 10e87d0c..5514c01a 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -1,490 +1,120 @@
/*
- * Preferences for dillo
+ * Preferences
*
- * Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <stdlib.h>
-#include <string.h> /* for strchr */
-#include <fcntl.h>
-#include <unistd.h>
-#include <locale.h> /* for setlocale */
-#include <ctype.h> /* for isspace */
#include "prefs.h"
-#include "colors.h"
-#include "misc.h"
-#include "msg.h"
-
-#define RCNAME "dillorc"
-
-#define DILLO_START_PAGE "about:splash"
-#define DILLO_HOME "http://www.dillo.org/"
-#define D_GEOMETRY_DEFAULT_WIDTH 780
-#define D_GEOMETRY_DEFAULT_HEIGHT 580
-#define D_GEOMETRY_DEFAULT_XPOS -9999
-#define D_GEOMETRY_DEFAULT_YPOS -9999
-
-#define D_VW_FONTNAME "DejaVu Sans"
-#define D_FW_FONTNAME "DejaVu Sans Mono"
-#define D_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
-#define D_SAVE_DIR "/tmp/"
-#define DW_COLOR_DEFAULT_BGND 0xdcd1ba
-#define DW_COLOR_DEFAULT_TEXT 0x000000
-#define DW_COLOR_DEFAULT_LINK 0x0000ff
-#define DW_COLOR_DEFAULT_VLINK 0x800080
+#define PREFS_START_PAGE "about:splash"
+#define PREFS_HOME "http://www.dillo.org/"
+#define PREFS_FONT_SERIF "DejaVu Serif"
+#define PREFS_FONT_SANS_SERIF "DejaVu Sans"
+#define PREFS_FONT_CURSIVE "URW Chancery L"
+#define PREFS_FONT_FANTASY "DejaVu Sans" /* TODO: find good default */
+#define PREFS_FONT_MONOSPACE "DejaVu Sans Mono"
+#define PREFS_SEARCH_URL "http://www.google.com/search?ie=UTF-8&oe=UTF-8&q=%s"
+#define PREFS_NO_PROXY "localhost 127.0.0.1"
+#define PREFS_SAVE_DIR "/tmp/"
+#define PREFS_HTTP_REFERER "host"
+#define PREFS_HTTP_USER_AGENT "Dillo/" VERSION
/*-----------------------------------------------------------------------------
* Global Data
*---------------------------------------------------------------------------*/
DilloPrefs prefs;
-/*-----------------------------------------------------------------------------
- * Local types
- *---------------------------------------------------------------------------*/
-
-/* define enumeration values to be returned for specific symbols */
-typedef enum {
- DRC_TOKEN_ALLOW_WHITE_BG,
- DRC_TOKEN_BG_COLOR,
- DRC_TOKEN_CONTRAST_VISITED_COLOR,
- DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT,
- DRC_TOKEN_FOCUS_NEW_TAB,
- DRC_TOKEN_FONT_FACTOR,
- DRC_TOKEN_FORCE_MY_COLORS,
- DRC_TOKEN_FULLWINDOW_START,
- DRC_TOKEN_FW_FONT,
- DRC_TOKEN_GENERATE_SUBMIT,
- DRC_TOKEN_GEOMETRY,
- DRC_TOKEN_HOME,
- DRC_TOKEN_LIMIT_TEXT_WIDTH,
- DRC_TOKEN_LINK_COLOR,
- DRC_TOKEN_LOAD_IMAGES,
- DRC_TOKEN_BUFFERED_DRAWING,
- DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB,
- DRC_TOKEN_NOPROXY,
- DRC_TOKEN_PANEL_SIZE,
- DRC_TOKEN_PROXY,
- DRC_TOKEN_PROXYUSER,
- DRC_TOKEN_REFERER,
- DRC_TOKEN_SAVE_DIR,
- DRC_TOKEN_SEARCH_URL,
- DRC_TOKEN_SHOW_BACK,
- DRC_TOKEN_SHOW_BOOKMARKS,
- DRC_TOKEN_SHOW_CLEAR_URL,
- DRC_TOKEN_SHOW_EXTRA_WARNINGS,
- DRC_TOKEN_SHOW_FORW,
- DRC_TOKEN_SHOW_HOME,
- DRC_TOKEN_SHOW_FILEMENU,
- DRC_TOKEN_SHOW_MSG,
- DRC_TOKEN_SHOW_PROGRESS_BOX,
- DRC_TOKEN_SHOW_RELOAD,
- DRC_TOKEN_SHOW_SAVE,
- DRC_TOKEN_SHOW_SEARCH,
- DRC_TOKEN_SHOW_STOP,
- DRC_TOKEN_SHOW_TOOLTIP,
- DRC_TOKEN_SHOW_URL,
- DRC_TOKEN_SMALL_ICONS,
- DRC_TOKEN_STANDARD_WIDGET_COLORS,
- DRC_TOKEN_START_PAGE,
- DRC_TOKEN_TEXT_COLOR,
- DRC_TOKEN_VISITED_COLOR,
- DRC_TOKEN_VW_FONT,
- DRC_TOKEN_W3C_PLUS_HEURISTICS
-} RcToken_t;
-
-typedef struct SymNode_ SymNode_t;
-
-struct SymNode_ {
- char *name;
- RcToken_t token;
-};
-
-/*-----------------------------------------------------------------------------
- * Local data
- *---------------------------------------------------------------------------*/
-
-/* Symbol array, sorted alphabetically */
-static const SymNode_t symbols[] = {
- { "allow_white_bg", DRC_TOKEN_ALLOW_WHITE_BG },
- { "bg_color", DRC_TOKEN_BG_COLOR },
- { "buffered_drawing", DRC_TOKEN_BUFFERED_DRAWING },
- { "contrast_visited_color", DRC_TOKEN_CONTRAST_VISITED_COLOR },
- { "enterpress_forces_submit", DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT },
- { "focus_new_tab", DRC_TOKEN_FOCUS_NEW_TAB },
- { "font_factor", DRC_TOKEN_FONT_FACTOR },
- { "force_my_colors", DRC_TOKEN_FORCE_MY_COLORS },
- { "fullwindow_start", DRC_TOKEN_FULLWINDOW_START },
- { "fw_fontname", DRC_TOKEN_FW_FONT },
- { "generate_submit", DRC_TOKEN_GENERATE_SUBMIT },
- { "geometry", DRC_TOKEN_GEOMETRY },
- { "home", DRC_TOKEN_HOME },
- { "http_proxy", DRC_TOKEN_PROXY },
- { "http_proxyuser", DRC_TOKEN_PROXYUSER },
- { "http_referer", DRC_TOKEN_REFERER },
- { "limit_text_width", DRC_TOKEN_LIMIT_TEXT_WIDTH },
- { "link_color", DRC_TOKEN_LINK_COLOR },
- { "load_images", DRC_TOKEN_LOAD_IMAGES },
- { "middle_click_opens_new_tab", DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB },
- { "no_proxy", DRC_TOKEN_NOPROXY },
- { "panel_size", DRC_TOKEN_PANEL_SIZE },
- { "save_dir", DRC_TOKEN_SAVE_DIR },
- { "search_url", DRC_TOKEN_SEARCH_URL },
- { "show_back", DRC_TOKEN_SHOW_BACK },
- { "show_bookmarks", DRC_TOKEN_SHOW_BOOKMARKS },
- { "show_clear_url", DRC_TOKEN_SHOW_CLEAR_URL },
- { "show_extra_warnings", DRC_TOKEN_SHOW_EXTRA_WARNINGS },
- { "show_filemenu", DRC_TOKEN_SHOW_FILEMENU },
- { "show_forw", DRC_TOKEN_SHOW_FORW },
- { "show_home", DRC_TOKEN_SHOW_HOME },
- { "show_msg", DRC_TOKEN_SHOW_MSG },
- { "show_progress_box", DRC_TOKEN_SHOW_PROGRESS_BOX },
- { "show_reload", DRC_TOKEN_SHOW_RELOAD },
- { "show_save", DRC_TOKEN_SHOW_SAVE },
- { "show_search", DRC_TOKEN_SHOW_SEARCH },
- { "show_stop", DRC_TOKEN_SHOW_STOP },
- { "show_tooltip", DRC_TOKEN_SHOW_TOOLTIP },
- { "show_url", DRC_TOKEN_SHOW_URL },
- { "small_icons", DRC_TOKEN_SMALL_ICONS },
- { "standard_widget_colors", DRC_TOKEN_STANDARD_WIDGET_COLORS },
- { "start_page", DRC_TOKEN_START_PAGE },
- { "text_color", DRC_TOKEN_TEXT_COLOR },
- { "visited_color", DRC_TOKEN_VISITED_COLOR, },
- { "vw_fontname", DRC_TOKEN_VW_FONT },
- { "w3c_plus_heuristics", DRC_TOKEN_W3C_PLUS_HEURISTICS }
-};
-
-static const uint_t n_symbols = sizeof (symbols) / sizeof (symbols[0]);
-
/*
- *- Mini parser -------------------------------------------------------------
+ * Sets the default settings.
*/
-/*
- * Comparison function for binary search
- */
-static int Prefs_symbol_cmp(const void *a, const void *b)
-{
- return strcmp(((SymNode_t*)a)->name, ((SymNode_t*)b)->name);
-}
-
-/*
- * Parse a name/value pair and set preferences accordingly.
- */
-static int Prefs_parse_pair(char *name, char *value)
-{
- int st;
- SymNode_t key, *node;
-
- key.name = name;
- node = bsearch(&key, symbols, n_symbols,
- sizeof(SymNode_t), Prefs_symbol_cmp);
- if (!node) {
- MSG("prefs: {%s} is not a recognized token.\n", name);
- return -1;
- }
-
- switch (node->token) {
- case DRC_TOKEN_GEOMETRY:
- a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
- &prefs.width, &prefs.height);
- break;
- case DRC_TOKEN_PROXY:
- a_Url_free(prefs.http_proxy);
- prefs.http_proxy = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_PROXYUSER:
- dFree(prefs.http_proxyuser);
- prefs.http_proxyuser = dStrdup(value);
- break;
- case DRC_TOKEN_REFERER:
- dFree(prefs.http_referer);
- prefs.http_referer = dStrdup(value);
- break;
- case DRC_TOKEN_NOPROXY:
- dFree(prefs.no_proxy);
- prefs.no_proxy = dStrdup(value);
- break;
- case DRC_TOKEN_LINK_COLOR:
- prefs.link_color = a_Color_parse(value, prefs.link_color, &st);
- break;
- case DRC_TOKEN_VISITED_COLOR:
- prefs.visited_color = a_Color_parse(value, prefs.visited_color, &st);
- break;
- case DRC_TOKEN_TEXT_COLOR:
- prefs.text_color = a_Color_parse(value, prefs.text_color, &st);
- break;
- case DRC_TOKEN_BG_COLOR:
- prefs.bg_color = a_Color_parse(value, prefs.bg_color, &st);
- break;
- case DRC_TOKEN_ALLOW_WHITE_BG:
- prefs.allow_white_bg = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FORCE_MY_COLORS:
- prefs.force_my_colors = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_CONTRAST_VISITED_COLOR:
- prefs.contrast_visited_color = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_STANDARD_WIDGET_COLORS:
- prefs.standard_widget_colors = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_PANEL_SIZE:
- if (!dStrcasecmp(value, "tiny"))
- prefs.panel_size = P_tiny;
- else if (!dStrcasecmp(value, "small"))
- prefs.panel_size = P_small;
- else if (!dStrcasecmp(value, "medium"))
- prefs.panel_size = P_medium;
- else /* default to "large" */
- prefs.panel_size = P_large;
- break;
- case DRC_TOKEN_SMALL_ICONS:
- prefs.small_icons = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_START_PAGE:
- a_Url_free(prefs.start_page);
- prefs.start_page = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_HOME:
- a_Url_free(prefs.home);
- prefs.home = a_Url_new(value, NULL);
- break;
- case DRC_TOKEN_SHOW_TOOLTIP:
- prefs.show_tooltip = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FOCUS_NEW_TAB:
- prefs.focus_new_tab = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FONT_FACTOR:
- prefs.font_factor = strtod(value, NULL);
- break;
- case DRC_TOKEN_LIMIT_TEXT_WIDTH:
- prefs.limit_text_width = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_W3C_PLUS_HEURISTICS:
- prefs.w3c_plus_heuristics = (strcmp(value,"YES") == 0);
- break;
- case DRC_TOKEN_SHOW_BACK:
- prefs.show_back = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_FORW:
- prefs.show_forw = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_HOME:
- prefs.show_home = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_RELOAD:
- prefs.show_reload = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_SAVE:
- prefs.show_save = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_STOP:
- prefs.show_stop = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_BOOKMARKS:
- prefs.show_bookmarks = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_FILEMENU:
- prefs.show_filemenu = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_CLEAR_URL:
- prefs.show_clear_url = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_URL:
- prefs.show_url = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_SEARCH:
- prefs.show_search = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_PROGRESS_BOX:
- prefs.show_progress_box = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_FULLWINDOW_START:
- prefs.fullwindow_start = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_LOAD_IMAGES:
- prefs.load_images = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_BUFFERED_DRAWING:
- prefs.buffered_drawing = atoi(value);
- break;
- case DRC_TOKEN_FW_FONT:
- dFree(prefs.fw_fontname);
- prefs.fw_fontname = dStrdup(value);
- break;
- case DRC_TOKEN_VW_FONT:
- dFree(prefs.vw_fontname);
- prefs.vw_fontname = dStrdup(value);
- break;
- case DRC_TOKEN_GENERATE_SUBMIT:
- prefs.generate_submit = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_ENTERPRESS_FORCES_SUBMIT:
- prefs.enterpress_forces_submit = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_MIDDLE_CLICK_OPENS_NEW_TAB:
- prefs.middle_click_opens_new_tab = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SEARCH_URL:
- dFree(prefs.search_url);
- prefs.search_url = dStrdup(value);
- break;
- case DRC_TOKEN_SAVE_DIR:
- dFree(prefs.save_dir);
- prefs.save_dir = dStrdup(value);
- case DRC_TOKEN_SHOW_MSG:
- prefs.show_msg = (strcmp(value, "YES") == 0);
- break;
- case DRC_TOKEN_SHOW_EXTRA_WARNINGS:
- prefs.show_extra_warnings = (strcmp(value, "YES") == 0);
- break;
- default:
- MSG_WARN("prefs: {%s} IS recognized but not handled!\n", name);
- break; /* Not reached */
- }
-
- return 0;
-}
-
-/*
- * Parse dillorc and set the values in the prefs structure.
- */
-static int Prefs_parse_dillorc(void)
-{
- FILE *F_in;
- char *filename, *line, *name, *value;
- int ret = -1;
-
- filename = dStrconcat(dGethomedir(), "/.dillo/", RCNAME, NULL);
- if (!(F_in = fopen(filename, "r"))) {
- MSG("prefs: Can't open %s file: %s\n", RCNAME, filename);
- if (!(F_in = fopen(DILLORC_SYS, "r"))) {
- MSG("prefs: Can't open %s file: %s\n", RCNAME, DILLORC_SYS);
- MSG("prefs: Using internal defaults.\n");
- } else {
- MSG("prefs: Using %s\n", DILLORC_SYS);
- }
- }
-
- if (F_in) {
- /* scan dillorc line by line */
- while ((line = dGetline(F_in)) != NULL) {
- if (dParser_get_rc_pair(&line, &name, &value) == 0) {
- _MSG("{%s}, {%s}\n", name, value);
- Prefs_parse_pair(name, value);
- } else if (line[0] && line[0] != '#' && (!name || !value)) {
- MSG("prefs: Syntax error in %s: name=\"%s\" value=\"%s\"\n",
- RCNAME, name, value);
- }
- dFree(line);
- }
- fclose(F_in);
- ret = 0;
- }
- dFree(filename);
-
- return ret;
-}
-
-/*---------------------------------------------------------------------------*/
-
void a_Prefs_init(void)
{
- char *old_locale;
-
- prefs.width = D_GEOMETRY_DEFAULT_WIDTH;
- prefs.height = D_GEOMETRY_DEFAULT_HEIGHT;
- prefs.xpos = D_GEOMETRY_DEFAULT_XPOS;
- prefs.ypos = D_GEOMETRY_DEFAULT_YPOS;
- prefs.http_proxy = NULL;
- prefs.http_proxyuser = NULL;
- prefs.http_referer = dStrdup("host");
- prefs.no_proxy = NULL;
- prefs.link_color = DW_COLOR_DEFAULT_LINK;
- prefs.visited_color = DW_COLOR_DEFAULT_VLINK;
- prefs.bg_color = DW_COLOR_DEFAULT_BGND;
- prefs.text_color = DW_COLOR_DEFAULT_TEXT;
- prefs.start_page = a_Url_new(DILLO_START_PAGE, NULL);
- prefs.home = a_Url_new(DILLO_HOME, NULL);
prefs.allow_white_bg = TRUE;
- prefs.force_my_colors = FALSE;
+ prefs.buffered_drawing = 1;
prefs.contrast_visited_color = TRUE;
- prefs.standard_widget_colors = FALSE;
- prefs.show_tooltip = TRUE;
- prefs.panel_size = P_large;
- prefs.small_icons = FALSE;
- prefs.limit_text_width = FALSE;
- prefs.w3c_plus_heuristics = TRUE;
+ prefs.enterpress_forces_submit = FALSE;
+ prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
prefs.focus_new_tab = TRUE;
+ prefs.font_cursive = dStrdup(PREFS_FONT_CURSIVE);
prefs.font_factor = 1.0;
- prefs.show_back=TRUE;
- prefs.show_forw=TRUE;
- prefs.show_home=TRUE;
- prefs.show_reload=TRUE;
- prefs.show_save=TRUE;
- prefs.show_stop=TRUE;
- prefs.show_bookmarks=TRUE;
- prefs.show_filemenu=TRUE;
- prefs.show_clear_url=TRUE;
- prefs.show_url=TRUE;
- prefs.show_search=TRUE;
- prefs.show_progress_box=TRUE;
- prefs.fullwindow_start=FALSE;
+ prefs.font_max_size = 100;
+ prefs.font_min_size = 6;
+ prefs.font_fantasy = dStrdup(PREFS_FONT_FANTASY);
+ prefs.font_monospace = dStrdup(PREFS_FONT_MONOSPACE);
+ prefs.font_sans_serif = dStrdup(PREFS_FONT_SANS_SERIF);
+ prefs.font_serif = dStrdup(PREFS_FONT_SERIF);
+ prefs.fullwindow_start = FALSE;
+
+ /* these four constitute the geometry */
+ prefs.width = PREFS_GEOMETRY_DEFAULT_WIDTH;
+ prefs.height = PREFS_GEOMETRY_DEFAULT_HEIGHT;
+ prefs.xpos = PREFS_GEOMETRY_DEFAULT_XPOS;
+ prefs.ypos = PREFS_GEOMETRY_DEFAULT_YPOS;
+
+ prefs.home = a_Url_new(PREFS_HOME, NULL);
+ prefs.http_language = NULL;
+ prefs.http_proxy = NULL;
+ prefs.http_max_conns = 6;
+ prefs.http_proxyuser = NULL;
+ prefs.http_referer = dStrdup(PREFS_HTTP_REFERER);
+ prefs.http_user_agent = dStrdup(PREFS_HTTP_USER_AGENT);
+ prefs.limit_text_width = FALSE;
prefs.load_images=TRUE;
- prefs.buffered_drawing=1;
- prefs.vw_fontname = dStrdup(D_VW_FONTNAME);
- prefs.fw_fontname = dStrdup(D_FW_FONTNAME);
- prefs.generate_submit = FALSE;
- prefs.enterpress_forces_submit = FALSE;
+ prefs.load_stylesheets=TRUE;
+ prefs.middle_click_drags_page = TRUE;
prefs.middle_click_opens_new_tab = TRUE;
- prefs.search_url = dStrdup(D_SEARCH_URL);
- prefs.save_dir = dStrdup(D_SAVE_DIR);
- prefs.show_msg = TRUE;
+ prefs.no_proxy = dStrdup(PREFS_NO_PROXY);
+ prefs.panel_size = P_medium;
+ prefs.parse_embedded_css=TRUE;
+ prefs.save_dir = dStrdup(PREFS_SAVE_DIR);
+ prefs.search_url = dStrdup(PREFS_SEARCH_URL);
+ prefs.show_back = TRUE;
+ prefs.show_bookmarks = TRUE;
+ prefs.show_clear_url = TRUE;
prefs.show_extra_warnings = FALSE;
-
- /* this locale stuff is to avoid parsing problems with float numbers */
- old_locale = dStrdup (setlocale (LC_NUMERIC, NULL));
- setlocale (LC_NUMERIC, "C");
-
- Prefs_parse_dillorc();
-
- setlocale (LC_NUMERIC, old_locale);
- dFree (old_locale);
-
+ prefs.show_filemenu=TRUE;
+ prefs.show_forw = TRUE;
+ prefs.show_help = TRUE;
+ prefs.show_home = TRUE;
+ prefs.show_msg = TRUE;
+ prefs.show_progress_box = TRUE;
+ prefs.show_reload = TRUE;
+ prefs.show_save = TRUE;
+ prefs.show_search = TRUE;
+ prefs.show_stop = TRUE;
+ prefs.show_tools = TRUE;
+ prefs.show_tooltip = TRUE;
+ prefs.show_url = TRUE;
+ prefs.small_icons = FALSE;
+ prefs.start_page = a_Url_new(PREFS_START_PAGE, NULL);
+ prefs.w3c_plus_heuristics = TRUE;
}
/*
- * Preferences memory-deallocation
+ * memory-deallocation
* (Call this one at exit time)
*/
void a_Prefs_freeall(void)
{
+ dFree(prefs.font_cursive);
+ dFree(prefs.font_fantasy);
+ dFree(prefs.font_monospace);
+ dFree(prefs.font_sans_serif);
+ dFree(prefs.font_serif);
+ a_Url_free(prefs.home);
+ dFree(prefs.http_language);
+ a_Url_free(prefs.http_proxy);
dFree(prefs.http_proxyuser);
dFree(prefs.http_referer);
+ dFree(prefs.http_user_agent);
dFree(prefs.no_proxy);
- a_Url_free(prefs.http_proxy);
- dFree(prefs.fw_fontname);
- dFree(prefs.vw_fontname);
- a_Url_free(prefs.start_page);
- a_Url_free(prefs.home);
- dFree(prefs.search_url);
dFree(prefs.save_dir);
+ dFree(prefs.search_url);
+ a_Url_free(prefs.start_page);
}
diff --git a/src/prefs.h b/src/prefs.h
index 7a409631..684262ed 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -1,3 +1,14 @@
+/*
+ * Preferences
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
#ifndef __PREFS_H__
#define __PREFS_H__
@@ -7,9 +18,17 @@
extern "C" {
#endif /* __cplusplus */
+#define PREFS_GEOMETRY_DEFAULT_WIDTH 780
+#define PREFS_GEOMETRY_DEFAULT_HEIGHT 580
+#define PREFS_GEOMETRY_DEFAULT_XPOS -9999
+#define PREFS_GEOMETRY_DEFAULT_YPOS -9999
+
/* Panel sizes */
enum { P_tiny = 0, P_small, P_medium, P_large };
+enum {PREFS_FILTER_ALLOW_ALL,
+ PREFS_FILTER_SAME_DOMAIN};
+
typedef struct _DilloPrefs DilloPrefs;
struct _DilloPrefs {
@@ -17,20 +36,17 @@ struct _DilloPrefs {
int height;
int xpos;
int ypos;
+ char *http_language;
+ int32_t http_max_conns;
DilloUrl *http_proxy;
char *http_proxyuser;
char *http_referer;
+ char *http_user_agent;
char *no_proxy;
DilloUrl *start_page;
DilloUrl *home;
- int32_t link_color;
- int32_t visited_color;
- int32_t bg_color;
- int32_t text_color;
bool_t allow_white_bg;
- bool_t force_my_colors;
bool_t contrast_visited_color;
- bool_t standard_widget_colors;
bool_t show_tooltip;
int panel_size;
bool_t small_icons;
@@ -38,6 +54,8 @@ struct _DilloPrefs {
bool_t w3c_plus_heuristics;
bool_t focus_new_tab;
double font_factor;
+ int32_t font_max_size;
+ int32_t font_min_size;
bool_t show_back;
bool_t show_forw;
bool_t show_home;
@@ -45,23 +63,31 @@ struct _DilloPrefs {
bool_t show_save;
bool_t show_stop;
bool_t show_bookmarks;
+ bool_t show_tools;
bool_t show_filemenu;
bool_t show_clear_url;
bool_t show_url;
bool_t show_search;
+ bool_t show_help;
bool_t show_progress_box;
bool_t fullwindow_start;
bool_t load_images;
+ bool_t load_stylesheets;
+ bool_t parse_embedded_css;
+ int filter_auto_requests;
int32_t buffered_drawing;
- char *vw_fontname;
- char *fw_fontname;
- bool_t generate_submit;
+ char *font_serif;
+ char *font_sans_serif;
+ char *font_cursive;
+ char *font_fantasy;
+ char *font_monospace;
bool_t enterpress_forces_submit;
bool_t middle_click_opens_new_tab;
char *search_url;
char *save_dir;
bool_t show_msg;
bool_t show_extra_warnings;
+ bool_t middle_click_drags_page;
};
/* Global Data */
diff --git a/src/prefsparser.cc b/src/prefsparser.cc
new file mode 100644
index 00000000..78cade0b
--- /dev/null
+++ b/src/prefsparser.cc
@@ -0,0 +1,208 @@
+/*
+ * Preferences parser
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <locale.h> /* for setlocale */
+
+#include "prefs.h"
+#include "misc.h"
+#include "msg.h"
+
+#include "prefsparser.hh"
+
+typedef enum {
+ PREFS_BOOL,
+ PREFS_STRING,
+ PREFS_URL,
+ PREFS_INT32,
+ PREFS_DOUBLE,
+ PREFS_GEOMETRY,
+ PREFS_FILTER,
+ PREFS_PANEL_SIZE
+} PrefType_t;
+
+typedef struct SymNode_ {
+ const char *name;
+ void *pref;
+ PrefType_t type;
+} SymNode_t;
+
+/*
+ * Parse a name/value pair and set preferences accordingly.
+ */
+int PrefsParser::parseOption(char *name, char *value)
+{
+ const SymNode_t *node;
+ uint_t i;
+
+ /* Symbol array, sorted alphabetically */
+ const SymNode_t symbols[] = {
+ { "allow_white_bg", &prefs.allow_white_bg, PREFS_BOOL },
+ { "buffered_drawing", &prefs.buffered_drawing, PREFS_INT32 },
+ { "contrast_visited_color", &prefs.contrast_visited_color, PREFS_BOOL },
+ { "enterpress_forces_submit", &prefs.enterpress_forces_submit,
+ PREFS_BOOL },
+ { "filter_auto_requests", &prefs.filter_auto_requests, PREFS_FILTER },
+ { "focus_new_tab", &prefs.focus_new_tab, PREFS_BOOL },
+ { "font_cursive", &prefs.font_cursive, PREFS_STRING },
+ { "font_factor", &prefs.font_factor, PREFS_DOUBLE },
+ { "font_fantasy", &prefs.font_fantasy, PREFS_STRING },
+ { "font_max_size", &prefs.font_max_size, PREFS_INT32 },
+ { "font_min_size", &prefs.font_min_size, PREFS_INT32 },
+ { "font_monospace", &prefs.font_monospace, PREFS_STRING },
+ { "font_sans_serif", &prefs.font_sans_serif, PREFS_STRING },
+ { "font_serif", &prefs.font_serif, PREFS_STRING },
+ { "fullwindow_start", &prefs.fullwindow_start, PREFS_BOOL },
+ { "geometry", NULL, PREFS_GEOMETRY },
+ { "home", &prefs.home, PREFS_URL },
+ { "http_language", &prefs.http_language, PREFS_STRING },
+ { "http_max_conns", &prefs.http_max_conns, PREFS_INT32 },
+ { "http_proxy", &prefs.http_proxy, PREFS_URL },
+ { "http_proxyuser", &prefs.http_proxyuser, PREFS_STRING },
+ { "http_referer", &prefs.http_referer, PREFS_STRING },
+ { "http_user_agent", &prefs.http_user_agent, PREFS_STRING },
+ { "limit_text_width", &prefs.limit_text_width, PREFS_BOOL },
+ { "load_images", &prefs.load_images, PREFS_BOOL },
+ { "load_stylesheets", &prefs.load_stylesheets, PREFS_BOOL },
+ { "middle_click_drags_page", &prefs.middle_click_drags_page,
+ PREFS_BOOL },
+ { "middle_click_opens_new_tab", &prefs.middle_click_opens_new_tab,
+ PREFS_BOOL },
+ { "no_proxy", &prefs.no_proxy, PREFS_STRING },
+ { "panel_size", &prefs.panel_size, PREFS_PANEL_SIZE },
+ { "parse_embedded_css", &prefs.parse_embedded_css, PREFS_BOOL },
+ { "save_dir", &prefs.save_dir, PREFS_STRING },
+ { "search_url", &prefs.search_url, PREFS_STRING },
+ { "show_back", &prefs.show_back, PREFS_BOOL },
+ { "show_bookmarks", &prefs.show_bookmarks, PREFS_BOOL },
+ { "show_clear_url", &prefs.show_clear_url, PREFS_BOOL },
+ { "show_extra_warnings", &prefs.show_extra_warnings, PREFS_BOOL },
+ { "show_filemenu", &prefs.show_filemenu, PREFS_BOOL },
+ { "show_forw", &prefs.show_forw, PREFS_BOOL },
+ { "show_help", &prefs.show_help, PREFS_BOOL },
+ { "show_home", &prefs.show_home, PREFS_BOOL },
+ { "show_msg", &prefs.show_msg, PREFS_BOOL },
+ { "show_progress_box", &prefs.show_progress_box, PREFS_BOOL },
+ { "show_reload", &prefs.show_reload, PREFS_BOOL },
+ { "show_save", &prefs.show_save, PREFS_BOOL },
+ { "show_search", &prefs.show_search, PREFS_BOOL },
+ { "show_stop", &prefs.show_stop, PREFS_BOOL },
+ { "show_tools", &prefs.show_tools, PREFS_BOOL },
+ { "show_tooltip", &prefs.show_tooltip, PREFS_BOOL },
+ { "show_url", &prefs.show_url, PREFS_BOOL },
+ { "small_icons", &prefs.small_icons, PREFS_BOOL },
+ { "start_page", &prefs.start_page, PREFS_URL },
+ { "w3c_plus_heuristics", &prefs.w3c_plus_heuristics, PREFS_BOOL }
+ };
+
+ node = NULL;
+ for (i = 0; i < sizeof(symbols) / sizeof(SymNode_t); i++) {
+ if (!strcmp(symbols[i].name, name)) {
+ node = & (symbols[i]);
+ break;
+ }
+ }
+
+ if (!node) {
+ MSG("prefs: {%s} is not a recognized token.\n", name);
+ return -1;
+ }
+
+ switch (node->type) {
+ case PREFS_BOOL:
+ *(bool_t *)node->pref = (!dStrcasecmp(value, "yes") ||
+ !dStrcasecmp(value, "true"));
+ break;
+ case PREFS_STRING:
+ dFree(*(char **)node->pref);
+ *(char **)node->pref = dStrdup(value);
+ break;
+ case PREFS_URL:
+ a_Url_free(*(DilloUrl **)node->pref);
+ *(DilloUrl **)node->pref = a_Url_new(value, NULL);
+ break;
+ case PREFS_INT32:
+ *(int32_t *)node->pref = strtol(value, NULL, 10);
+ break;
+ case PREFS_DOUBLE:
+ *(double *)node->pref = strtod(value, NULL);
+ break;
+ case PREFS_GEOMETRY:
+ a_Misc_parse_geometry(value, &prefs.xpos, &prefs.ypos,
+ &prefs.width, &prefs.height);
+ break;
+ case PREFS_FILTER:
+ if (!dStrcasecmp(value, "same_domain"))
+ prefs.filter_auto_requests = PREFS_FILTER_SAME_DOMAIN;
+ else {
+ if (dStrcasecmp(value, "allow_all"))
+ MSG_WARN("prefs: unrecognized value for filter_auto_requests\n");
+ prefs.filter_auto_requests = PREFS_FILTER_ALLOW_ALL;
+ }
+ break;
+ case PREFS_PANEL_SIZE:
+ if (!dStrcasecmp(value, "tiny"))
+ prefs.panel_size = P_tiny;
+ else if (!dStrcasecmp(value, "small"))
+ prefs.panel_size = P_small;
+ else if (!dStrcasecmp(value, "large"))
+ prefs.panel_size = P_large;
+ else /* default to "medium" */
+ prefs.panel_size = P_medium;
+ break;
+ default:
+ MSG_WARN("prefs: {%s} IS recognized but not handled!\n", name);
+ break; /* Not reached */
+ }
+
+ if (prefs.limit_text_width) {
+ /* BUG: causes 100% CPU usage with <button> or <input type="image"> */
+ MSG_WARN("Disabling limit_text_width preference (currently broken).\n");
+ prefs.limit_text_width = FALSE;
+ }
+
+ return 0;
+}
+
+/*
+ * Parses the dillorc and sets the values in the prefs structure.
+ */
+void PrefsParser::parse(FILE *fp)
+{
+ char *line, *name, *value, *oldLocale;
+ int st;
+
+ // changing the LC_NUMERIC locale (temporarily) to C
+ // avoids parsing problems with float numbers
+ oldLocale = dStrdup(setlocale(LC_NUMERIC, NULL));
+ setlocale(LC_NUMERIC, "C");
+
+ // scan the file line by line
+ while ((line = dGetline(fp)) != NULL) {
+ st = dParser_parse_rc_line(&line, &name, &value);
+
+ if (st == 0) {
+ _MSG("prefsparser: name=%s, value=%s\n", name, value);
+ parseOption(name, value);
+ } else if (st < 0) {
+ MSG_ERR("prefsparser: Syntax error in dillorc:"
+ " name=\"%s\" value=\"%s\"\n", name, value);
+ }
+
+ dFree(line);
+ }
+ fclose(fp);
+
+ // restore the old numeric locale
+ setlocale(LC_NUMERIC, oldLocale);
+ dFree(oldLocale);
+}
diff --git a/src/prefsparser.hh b/src/prefsparser.hh
new file mode 100644
index 00000000..d10c43c4
--- /dev/null
+++ b/src/prefsparser.hh
@@ -0,0 +1,24 @@
+/*
+ * Preferences parser
+ *
+ * Copyright (C) 2006-2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PREFS_HH__
+#define __PREFS_HH__
+
+#ifdef __cplusplus
+class PrefsParser {
+public:
+ static int parseOption(char *name, char *value);
+ static int parseLine(char *line, char *name, char *value);
+ static void parse(FILE *fp);
+};
+#endif /* __cplusplus */
+
+#endif /* __PREFS_HH__ */
diff --git a/src/styleengine.cc b/src/styleengine.cc
new file mode 100644
index 00000000..906f47ee
--- /dev/null
+++ b/src/styleengine.cc
@@ -0,0 +1,678 @@
+/*
+ * File: styleengine.cc
+ *
+ * Copyright 2008-2009 Johannes Hofmann <Johannes.Hofmann@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "../dlib/dlib.h"
+#include "msg.h"
+#include "prefs.h"
+#include "html_common.hh"
+#include "styleengine.hh"
+
+using namespace dw::core::style;
+
+StyleEngine::StyleEngine (dw::core::Layout *layout) {
+ StyleAttrs style_attrs;
+ FontAttrs font_attrs;
+
+ doctree = new Doctree ();
+ stack = new lout::misc::SimpleVector <Node> (1);
+ cssContext = new CssContext ();
+ this->layout = layout;
+ importDepth = 0;
+
+ stack->increase ();
+ Node *n = stack->getRef (stack->size () - 1);
+
+ /* Create a dummy font, attribute, and tag for the bottom of the stack. */
+ font_attrs.name = prefs.font_sans_serif;
+ font_attrs.size = (int) (14 * prefs.font_factor + 0.5);
+ if (font_attrs.size < prefs.font_min_size)
+ font_attrs.size = prefs.font_min_size;
+ if (font_attrs.size > prefs.font_max_size)
+ font_attrs.size = prefs.font_max_size;
+ font_attrs.weight = 400;
+ font_attrs.style = FONT_STYLE_NORMAL;
+ font_attrs.letterSpacing = 0;
+
+ style_attrs.initValues ();
+ style_attrs.font = Font::create (layout, &font_attrs);
+ style_attrs.color = Color::create (layout, 0);
+ style_attrs.backgroundColor = Color::create (layout, 0xffffff);
+
+ n->style = Style::create (layout, &style_attrs);
+ n->wordStyle = NULL;
+ n->styleAttribute = NULL;
+ n->inheritBackgroundColor = false;
+}
+
+StyleEngine::~StyleEngine () {
+ while (doctree->top ())
+ endElement (doctree->top ()->element);
+ delete stack;
+ delete doctree;
+ delete cssContext;
+}
+
+/**
+ * \brief tell the styleEngine that a new html element has started.
+ */
+void StyleEngine::startElement (int element) {
+ if (stack->getRef (stack->size () - 1)->style == NULL)
+ style0 ();
+
+ stack->increase ();
+ Node *n = stack->getRef (stack->size () - 1);
+ n->style = NULL;
+ n->wordStyle = NULL;
+ n->styleAttribute = NULL;
+ n->inheritBackgroundColor = false;
+
+ DoctreeNode *dn = doctree->push ();
+ dn->element = element;
+}
+
+void StyleEngine::startElement (const char *tagname) {
+ startElement (a_Html_tag_index (tagname));
+}
+
+void StyleEngine::setId (const char *id) {
+ DoctreeNode *dn = doctree->top ();
+ assert (dn->id == NULL);
+ dn->id = dStrdup (id);
+};
+
+/**
+ * \brief split a string at sep chars and return a SimpleVector of strings
+ */
+static lout::misc::SimpleVector<char *> *splitStr (const char *str, char sep) {
+ const char *p1 = NULL;
+ lout::misc::SimpleVector<char *> *list =
+ new lout::misc::SimpleVector<char *> (1);
+
+ for (;; str++) {
+ if (*str != '\0' && *str != sep) {
+ if (!p1)
+ p1 = str;
+ } else if (p1) {
+ list->increase ();
+ list->set (list->size () - 1, dStrndup (p1, str - p1));
+ p1 = NULL;
+ }
+
+ if (*str == '\0')
+ break;
+ }
+
+ return list;
+}
+
+void StyleEngine::setClass (const char *klass) {
+ DoctreeNode *dn = doctree->top ();
+ assert (dn->klass == NULL);
+ dn->klass = splitStr (klass, ' ');
+};
+
+void StyleEngine::setStyle (const char *style) {
+ Node *n = stack->getRef (stack->size () - 1);
+ assert (n->styleAttribute == NULL);
+ n->styleAttribute = dStrdup (style);
+};
+
+/**
+ * \brief set properties that were definded using (mostly deprecated) HTML
+ * attributes (e.g. bgColor).
+ */
+void StyleEngine::setNonCssHints (CssPropertyList *nonCssHints) {
+ if (stack->getRef (stack->size () - 1)->style)
+ stack->getRef (stack->size () - 1)->style->unref ();
+ style0 (nonCssHints); // evaluate now, so caller can free nonCssHints
+}
+
+/**
+ * \brief Use of the background color of the parent style as default.
+ * This is only used in table code to allow for colors specified for
+ * table rows as table rows are currently no widgets and therefore
+ * don't draw any background.
+ */
+void StyleEngine::inheritBackgroundColor () {
+ stack->getRef (stack->size () - 1)->inheritBackgroundColor = true;
+}
+
+/**
+ * \brief set the CSS pseudo class :link.
+ */
+void StyleEngine::setPseudoLink () {
+ DoctreeNode *dn = doctree->top ();
+ dn->pseudo = "link";
+}
+
+/**
+ * \brief set the CSS pseudo class :visited.
+ */
+void StyleEngine::setPseudoVisited () {
+ DoctreeNode *dn = doctree->top ();
+ dn->pseudo = "visited";
+}
+
+/**
+ * \brief tell the styleEngine that a html element has ended.
+ */
+void StyleEngine::endElement (int element) {
+ assert (stack->size () > 0);
+ assert (element == doctree->top ()->element);
+
+ Node *n = stack->getRef (stack->size () - 1);
+
+ if (n->style)
+ n->style->unref ();
+ if (n->wordStyle)
+ n->wordStyle->unref ();
+ if (n->styleAttribute)
+ dFree ((void*) n->styleAttribute);
+
+ doctree->pop ();
+ stack->setSize (stack->size () - 1);
+}
+
+/**
+ * \brief Make changes to StyleAttrs attrs according to CssPropertyList props.
+ */
+void StyleEngine::apply (StyleAttrs *attrs, CssPropertyList *props) {
+ FontAttrs fontAttrs = *attrs->font;
+ Font *parentFont = stack->get (stack->size () - 2).style->font;
+ char *c, *fontName;
+ int lineHeight;
+
+ /* Determine font first so it can be used to resolve relative lenths. */
+ for (int i = 0; i < props->size (); i++) {
+ CssProperty *p = props->getRef (i);
+
+ switch (p->name) {
+ case CSS_PROPERTY_FONT_FAMILY:
+ // Check font names in comma separated list.
+ // Note, that p->value.strVal is modified, so that in future calls
+ // the matching font name can be used directly.
+ fontName = NULL;
+ while (p->value.strVal) {
+ if ((c = strchr(p->value.strVal, ',')))
+ *c = '\0';
+ dStrstrip(p->value.strVal);
+
+ if (strcmp (p->value.strVal, "serif") == 0)
+ fontName = prefs.font_serif;
+ else if (strcmp (p->value.strVal, "sans-serif") == 0)
+ fontName = prefs.font_sans_serif;
+ else if (strcmp (p->value.strVal, "cursive") == 0)
+ fontName = prefs.font_cursive;
+ else if (strcmp (p->value.strVal, "fantasy") == 0)
+ fontName = prefs.font_fantasy;
+ else if (strcmp (p->value.strVal, "monospace") == 0)
+ fontName = prefs.font_monospace;
+ else if (Font::exists(layout, p->value.strVal))
+ fontName = p->value.strVal;
+
+ if (fontName) { // font found
+ fontAttrs.name = fontName;
+ break;
+ } else if (c) { // try next from list
+ memmove(p->value.strVal, c + 1, strlen(c + 1) + 1);
+ } else { // no font found
+ break;
+ }
+ }
+
+ break;
+ case CSS_PROPERTY_FONT_SIZE:
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_FONT_SIZE_XX_SMALL:
+ fontAttrs.size = (int) (11.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_X_SMALL:
+ fontAttrs.size = (int) (12.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_SMALL:
+ fontAttrs.size = (int) (13.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_MEDIUM:
+ fontAttrs.size = (int) (14.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_LARGE:
+ fontAttrs.size = (int) (15.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_X_LARGE:
+ fontAttrs.size = (int) (16.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_XX_LARGE:
+ fontAttrs.size = (int) (17.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_SMALLER:
+ fontAttrs.size -= (int) (1.0 * prefs.font_factor + 0.5);
+ break;
+ case CSS_FONT_SIZE_LARGER:
+ fontAttrs.size += (int) (1.0 * prefs.font_factor + 0.5);
+ break;
+ default:
+ assert(false); // invalid font-size enum
+ }
+ } else {
+ computeValue (&fontAttrs.size, p->value.intVal, parentFont,
+ parentFont->size);
+ }
+
+ if (fontAttrs.size < prefs.font_min_size)
+ fontAttrs.size = prefs.font_min_size;
+ if (fontAttrs.size > prefs.font_max_size)
+ fontAttrs.size = prefs.font_max_size;
+
+ break;
+ case CSS_PROPERTY_FONT_STYLE:
+ fontAttrs.style = (FontStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_FONT_WEIGHT:
+
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_FONT_WEIGHT_BOLD:
+ fontAttrs.weight = 700;
+ break;
+ case CSS_FONT_WEIGHT_BOLDER:
+ fontAttrs.weight += 300;
+ break;
+ case CSS_FONT_WEIGHT_LIGHT:
+ fontAttrs.weight = 100;
+ break;
+ case CSS_FONT_WEIGHT_LIGHTER:
+ fontAttrs.weight -= 300;
+ break;
+ case CSS_FONT_WEIGHT_NORMAL:
+ fontAttrs.weight = 400;
+ break;
+ default:
+ assert(false); // invalid font weight value
+ break;
+ }
+ } else {
+ fontAttrs.weight = p->value.intVal;
+ }
+
+ if (fontAttrs.weight < 100)
+ fontAttrs.weight = 100;
+ if (fontAttrs.weight > 900)
+ fontAttrs.weight = 900;
+
+ break;
+ case CSS_PROPERTY_LETTER_SPACING:
+ if (p->type == CSS_TYPE_ENUM) {
+ if (p->value.intVal == CSS_LETTER_SPACING_NORMAL) {
+ fontAttrs.letterSpacing = 0;
+ }
+ } else {
+ computeValue (&fontAttrs.letterSpacing, p->value.intVal,
+ parentFont, parentFont->size);
+ }
+
+ /* Limit letterSpacing to reasonable values to avoid overflows e.g,
+ * when measuring word width.
+ */
+ if (fontAttrs.letterSpacing > 1000)
+ fontAttrs.letterSpacing = 1000;
+ else if (fontAttrs.letterSpacing < -1000)
+ fontAttrs.letterSpacing = -1000;
+ break;
+ default:
+ break;
+ }
+ }
+
+ attrs->font = Font::create (layout, &fontAttrs);
+
+ for (int i = 0; i < props->size (); i++) {
+ CssProperty *p = props->getRef (i);
+
+ switch (p->name) {
+ /* \todo missing cases */
+ case CSS_PROPERTY_BACKGROUND_COLOR:
+ if (prefs.allow_white_bg || p->value.intVal != 0xffffff)
+ attrs->backgroundColor = Color::create(layout, p->value.intVal);
+ else
+ //attrs->backgroundColor = Color::create(layout, 0xdcd1ba);
+ attrs->backgroundColor = Color::create(layout, 0xe0e0a3);
+ break;
+ case CSS_PROPERTY_BORDER_TOP_COLOR:
+ attrs->borderColor.top =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_COLOR:
+ attrs->borderColor.bottom =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_COLOR:
+ attrs->borderColor.left =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_COLOR:
+ attrs->borderColor.right =
+ Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_STYLE:
+ attrs->borderStyle.bottom = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_STYLE:
+ attrs->borderStyle.left = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_STYLE:
+ attrs->borderStyle.right = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_TOP_STYLE:
+ attrs->borderStyle.top = (BorderStyle) p->value.intVal;
+ break;
+ case CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.bottom, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_LEFT_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.left, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_RIGHT_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.right, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_TOP_WIDTH:
+ computeBorderWidth (&attrs->borderWidth.top, p, attrs->font);
+ break;
+ case CSS_PROPERTY_BORDER_SPACING:
+ computeValue (&attrs->hBorderSpacing, p->value.intVal,attrs->font);
+ computeValue (&attrs->vBorderSpacing, p->value.intVal,attrs->font);
+ break;
+ case CSS_PROPERTY_COLOR:
+ attrs->color = Color::create (layout, p->value.intVal);
+ break;
+ case CSS_PROPERTY_CURSOR:
+ attrs->cursor = (Cursor) p->value.intVal;
+ break;
+ case CSS_PROPERTY_DISPLAY:
+ attrs->display = (DisplayType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_LINE_HEIGHT:
+ if (p->type == CSS_TYPE_ENUM) { //only valid enum value is "normal"
+ attrs->lineHeight = dw::core::style::LENGTH_AUTO;
+ } else if (p->type == CSS_TYPE_LENGTH_PERCENTAGE_NUMBER) {
+ if (CSS_LENGTH_TYPE (p->value.intVal) == CSS_LENGTH_TYPE_NONE) {
+ attrs->lineHeight =
+ createPerLength(CSS_LENGTH_VALUE(p->value.intVal));
+ } else {
+ computeValue (&lineHeight, p->value.intVal, attrs->font,
+ attrs->font->size);
+ attrs->lineHeight = createAbsLength(lineHeight);
+ }
+ }
+ break;
+ case CSS_PROPERTY_LIST_STYLE_POSITION:
+ attrs->listStylePosition = (ListStylePosition) p->value.intVal;
+ break;
+ case CSS_PROPERTY_LIST_STYLE_TYPE:
+ attrs->listStyleType = (ListStyleType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_MARGIN_BOTTOM:
+ computeValue (&attrs->margin.bottom, p->value.intVal, attrs->font);
+ if (attrs->margin.bottom < 0) // \todo fix negative margins in dw/*
+ attrs->margin.bottom = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_LEFT:
+ computeValue (&attrs->margin.left, p->value.intVal, attrs->font);
+ if (attrs->margin.left < 0) // \todo fix negative margins in dw/*
+ attrs->margin.left = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_RIGHT:
+ computeValue (&attrs->margin.right, p->value.intVal, attrs->font);
+ if (attrs->margin.right < 0) // \todo fix negative margins in dw/*
+ attrs->margin.right = 0;
+ break;
+ case CSS_PROPERTY_MARGIN_TOP:
+ computeValue (&attrs->margin.top, p->value.intVal, attrs->font);
+ if (attrs->margin.top < 0) // \todo fix negative margins in dw/*
+ attrs->margin.top = 0;
+ break;
+ case CSS_PROPERTY_PADDING_TOP:
+ computeValue (&attrs->padding.top, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_BOTTOM:
+ computeValue (&attrs->padding.bottom, p->value.intVal,attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_LEFT:
+ computeValue (&attrs->padding.left, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_PADDING_RIGHT:
+ computeValue (&attrs->padding.right, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_TEXT_ALIGN:
+ attrs->textAlign = (TextAlignType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_TEXT_DECORATION:
+ attrs->textDecoration |= p->value.intVal;
+ break;
+ case CSS_PROPERTY_VERTICAL_ALIGN:
+ attrs->valign = (VAlignType) p->value.intVal;
+ break;
+ case CSS_PROPERTY_WHITE_SPACE:
+ attrs->whiteSpace = (WhiteSpace) p->value.intVal;
+ break;
+ case CSS_PROPERTY_WIDTH:
+ computeLength (&attrs->width, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_HEIGHT:
+ computeLength (&attrs->height, p->value.intVal, attrs->font);
+ break;
+ case CSS_PROPERTY_WORD_SPACING:
+ if (p->type == CSS_TYPE_ENUM) {
+ if (p->value.intVal == CSS_WORD_SPACING_NORMAL) {
+ attrs->wordSpacing = 0;
+ }
+ } else {
+ computeValue(&attrs->wordSpacing, p->value.intVal, attrs->font);
+ }
+
+ /* Limit to reasonable values to avoid overflows */
+ if (attrs->wordSpacing > 1000)
+ attrs->wordSpacing = 1000;
+ else if (attrs->wordSpacing < -1000)
+ attrs->wordSpacing = -1000;
+ break;
+ case PROPERTY_X_LINK:
+ attrs->x_link = p->value.intVal;
+ break;
+ case PROPERTY_X_IMG:
+ attrs->x_img = p->value.intVal;
+ break;
+ case PROPERTY_X_TOOLTIP:
+ attrs->x_tooltip = Tooltip::create(layout, p->value.strVal);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* make sure border colors are set */
+ if (attrs->borderColor.top == NULL)
+ attrs->borderColor.top = attrs->color;
+ if (attrs->borderColor.bottom == NULL)
+ attrs->borderColor.bottom = attrs->color;
+ if (attrs->borderColor.left == NULL)
+ attrs->borderColor.left = attrs->color;
+ if (attrs->borderColor.right == NULL)
+ attrs->borderColor.right = attrs->color;
+
+}
+
+/**
+ * \brief Resolve relative lengths to absolute values.
+ */
+bool StyleEngine::computeValue (int *dest, CssLength value, Font *font) {
+ static float dpmm;
+
+ if (dpmm == 0.0)
+ dpmm = layout->dpiX () / 25.4; /* assume dpiX == dpiY */
+
+ switch (CSS_LENGTH_TYPE (value)) {
+ case CSS_LENGTH_TYPE_PX:
+ *dest = (int) CSS_LENGTH_VALUE (value);
+ return true;
+ case CSS_LENGTH_TYPE_MM:
+ *dest = (int) (CSS_LENGTH_VALUE (value) * dpmm + 0.5);
+ return true;
+ case CSS_LENGTH_TYPE_EM:
+ *dest = (int) (CSS_LENGTH_VALUE (value) * font->size + 0.5);
+ return true;
+ case CSS_LENGTH_TYPE_EX:
+ *dest = (int) (CSS_LENGTH_VALUE(value) * font->xHeight + 0.5);
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+bool StyleEngine::computeValue (int *dest, CssLength value, Font *font,
+ int percentageBase) {
+ if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
+ *dest = (int) (CSS_LENGTH_VALUE (value) * percentageBase + 0.5);
+ return true;
+ } else
+ return computeValue (dest, value, font);
+}
+
+bool StyleEngine::computeLength (dw::core::style::Length *dest,
+ CssLength value, Font *font) {
+ int v;
+
+ if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_PERCENTAGE) {
+ *dest = createPerLength (CSS_LENGTH_VALUE (value));
+ return true;
+ } else if (CSS_LENGTH_TYPE (value) == CSS_LENGTH_TYPE_AUTO) {
+ *dest = dw::core::style::LENGTH_AUTO;
+ return true;
+ } else if (computeValue (&v, value, font)) {
+ *dest = createAbsLength (v);
+ return true;
+ }
+
+ return false;
+}
+
+void StyleEngine::computeBorderWidth (int *dest, CssProperty *p,
+ dw::core::style::Font *font) {
+ if (p->type == CSS_TYPE_ENUM) {
+ switch (p->value.intVal) {
+ case CSS_BORDER_WIDTH_THIN:
+ *dest = 1;
+ break;
+ case CSS_BORDER_WIDTH_MEDIUM:
+ *dest = 2;
+ break;
+ case CSS_BORDER_WIDTH_THICK:
+ *dest = 3;
+ break;
+ default:
+ assert(false);
+ }
+ } else {
+ computeValue (dest, p->value.intVal, font);
+ }
+}
+
+/**
+ * \brief Similar to StyleEngine::style(), but with backgroundColor set.
+ * A normal style might have backgroundColor == NULL to indicate a transparent
+ * background. This method ensures that backgroundColor is set.
+ */
+Style * StyleEngine::backgroundStyle () {
+ StyleAttrs attrs = *style ();
+
+ for (int i = stack->size () - 1; i >= 0 && ! attrs.backgroundColor; i--)
+ attrs.backgroundColor = stack->getRef (i)->style->backgroundColor;
+
+ assert (attrs.backgroundColor);
+ return Style::create (layout, &attrs);
+}
+
+/**
+ * \brief Create a new style object based on the previously opened / closed
+ * HTML elements and the nonCssProperties that have been set.
+ * This method is private. Call style() to get a current style object.
+ */
+Style * StyleEngine::style0 (CssPropertyList *nonCssProperties) {
+ CssPropertyList props, *styleAttributeProps = NULL;
+ const char *styleAttribute =
+ stack->getRef (stack->size () - 1)->styleAttribute;
+ // get previous style from the stack
+ StyleAttrs attrs = *stack->getRef (stack->size () - 2)->style;
+
+ // Ensure that StyleEngine::style0() has not been called before for
+ // this element.
+ // Style computation is expensive so limit it as much as possible.
+ // If this assertion is hit, you need to rearrange the code that is
+ // doing styleEngine calls to call setNonCssHints() before calling
+ // style() or wordStyle() for each new element.
+ assert (stack->getRef (stack->size () - 1)->style == NULL);
+
+ // reset values that are not inherited according to CSS
+ attrs.resetValues ();
+
+ if (stack->getRef (stack->size () - 2)->inheritBackgroundColor) {
+ attrs.backgroundColor =
+ stack->getRef (stack->size () - 2)->style->backgroundColor;
+
+ attrs.valign = stack->getRef (stack->size () - 2)->style->valign;
+ }
+
+ // parse style information from style="" attribute, if it exists
+ if (styleAttribute && prefs.parse_embedded_css)
+ styleAttributeProps =
+ CssParser::parseDeclarationBlock (styleAttribute,
+ strlen (styleAttribute));
+
+ // merge style information
+ cssContext->apply (&props, doctree, styleAttributeProps, nonCssProperties);
+
+ // apply style
+ apply (&attrs, &props);
+
+ stack->getRef (stack->size () - 1)->style = Style::create (layout, &attrs);
+
+ if (styleAttributeProps)
+ delete styleAttributeProps;
+
+ return stack->getRef (stack->size () - 1)->style;
+}
+
+Style * StyleEngine::wordStyle0 (CssPropertyList *nonCssProperties) {
+ StyleAttrs attrs = *style ();
+ attrs.resetValues ();
+
+ if (stack->getRef (stack->size () - 1)->inheritBackgroundColor)
+ attrs.backgroundColor = style ()->backgroundColor;
+
+ attrs.valign = style ()->valign;
+
+ stack->getRef(stack->size() - 1)->wordStyle = Style::create(layout, &attrs);
+ return stack->getRef (stack->size () - 1)->wordStyle;
+}
+
+void StyleEngine::parse (DilloHtml *html, DilloUrl *url, const char *buf,
+ int buflen, CssOrigin origin) {
+ if (importDepth > 10) { // avoid looping with recursive @import directives
+ MSG_WARN("Maximum depth of CSS @import reached--ignoring stylesheet.\n");
+ return;
+ }
+
+ importDepth++;
+ CssParser::parse (html, url, cssContext, buf, buflen, origin);
+ importDepth--;
+}
diff --git a/src/styleengine.hh b/src/styleengine.hh
new file mode 100644
index 00000000..66f28cee
--- /dev/null
+++ b/src/styleengine.hh
@@ -0,0 +1,84 @@
+#ifndef __STYLEENGINE_HH__
+#define __STYLEENGINE_HH__
+
+class StyleEngine;
+
+#include "dw/core.hh"
+#include "doctree.hh"
+#include "css.hh"
+#include "cssparser.hh"
+
+/**
+ * \brief This class provides the glue between HTML parser and CSS subsystem.
+ *
+ * It maintains a document tree and creates and caches style objects for use
+ * by the HTML parser.
+ * The HTML parser in turn informs StyleEngine about opened or closed
+ * HTML elements and their attributes via the startElement() / endElement()
+ * methods.
+ */
+class StyleEngine {
+ private:
+ class Node {
+ public:
+ dw::core::style::Style *style;
+ dw::core::style::Style *wordStyle;
+ const char *styleAttribute;
+ bool inheritBackgroundColor;
+ };
+
+ dw::core::Layout *layout;
+ lout::misc::SimpleVector <Node> *stack;
+ CssContext *cssContext;
+ Doctree *doctree;
+ int importDepth;
+
+ dw::core::style::Style *style0 (CssPropertyList *nonCssHints = NULL);
+ dw::core::style::Style *wordStyle0 (CssPropertyList *nonCssHints = NULL);
+ void apply (dw::core::style::StyleAttrs *attrs, CssPropertyList *props);
+ bool computeValue (int *dest, CssLength value,
+ dw::core::style::Font *font);
+ bool computeValue (int *dest, CssLength value,
+ dw::core::style::Font *font, int percentageBase);
+ bool computeLength (dw::core::style::Length *dest, CssLength value,
+ dw::core::style::Font *font);
+ void computeBorderWidth (int *dest, CssProperty *p,
+ dw::core::style::Font *font);
+
+ public:
+ StyleEngine (dw::core::Layout *layout);
+ ~StyleEngine ();
+
+ void parse (DilloHtml *html, DilloUrl *url, const char *buf, int buflen,
+ CssOrigin origin);
+ void startElement (int tag);
+ void startElement (const char *tagname);
+ void setId (const char *id);
+ const char * getId () { return doctree->top ()->id; };
+ void setClass (const char *klass);
+ void setStyle (const char *style);
+ void endElement (int tag);
+ void setPseudoLink ();
+ void setPseudoVisited ();
+ void setNonCssHints (CssPropertyList *nonCssHints);
+ void inheritBackgroundColor (); /* \todo get rid of this somehow */
+ dw::core::style::Style *backgroundStyle ();
+
+ inline dw::core::style::Style *style () {
+ dw::core::style::Style *s = stack->getRef (stack->size () - 1)->style;
+ if (s)
+ return s;
+ else
+ return style0 ();
+ };
+
+ inline dw::core::style::Style *wordStyle () {
+ dw::core::style::Style *s = stack->getRef(stack->size()-1)->wordStyle;
+ if (s)
+ return s;
+ else
+ return wordStyle0 ();
+ };
+};
+
+#endif
diff --git a/src/table.cc b/src/table.cc
index 589f7e1e..43304206 100644
--- a/src/table.cc
+++ b/src/table.cc
@@ -18,40 +18,31 @@
#include "prefs.h"
#include "msg.h"
-
-/* Undefine if you want to unroll tables. For instance for PDAs */
-#define USE_TABLES
-
-#define dillo_dbg_rendering 0
+#include "css.hh"
using namespace dw;
using namespace dw::core;
using namespace dw::core::style;
/*
- * Forward declarations
+ * Forward declarations
*/
static void Html_tag_open_table_cell(DilloHtml *html,
const char *tag, int tagsize,
- dw::core::style::TextAlignType text_align);
+ dw::core::style::TextAlignType text_align);
/*
* <TABLE>
*/
void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
{
-#ifdef USE_TABLES
dw::core::Widget *table;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *cell_style, *old_style;
+ CssPropertyList props, *table_cell_props;
const char *attrbuf;
- int32_t border = 0, cellspacing = 1, cellpadding = 2, bgcolor;
-#endif
+ int32_t border = -1, cellspacing = -1, cellpadding = -1, bgcolor = -1;
+ CssLength cssLength;
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
-
-#ifdef USE_TABLES
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "border")))
border = isdigit(attrbuf[0]) ? strtol (attrbuf, NULL, 10) : 1;
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellspacing")))
@@ -59,73 +50,85 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "cellpadding")))
cellpadding = strtol (attrbuf, NULL, 10);
- /* The style for the table */
- style_attrs = *S_TOP(html)->style;
-
- /* When dillo was started with the --debug-rendering option, there
- * is always a border around the table. */
- if (dillo_dbg_rendering)
- style_attrs.borderWidth.setVal (MIN (border, 1));
- else
- style_attrs.borderWidth.setVal (border);
+ if (border != -1) {
+ cssLength = CSS_CREATE_LENGTH (border, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_TOP_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_LEFT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ props.set (CSS_PROPERTY_BORDER_RIGHT_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ }
- style_attrs.setBorderColor (
- Color::createShaded(HT2LT(html), S_TOP(html)->current_bg_color));
- style_attrs.setBorderStyle (BORDER_OUTSET);
- style_attrs.hBorderSpacing = cellspacing;
- style_attrs.vBorderSpacing = cellspacing;
+ if (cellspacing != -1) {
+ cssLength = CSS_CREATE_LENGTH (cellspacing, CSS_LENGTH_TYPE_PX);
+ props.set (CSS_PROPERTY_BORDER_SPACING, CSS_TYPE_LENGTH_PERCENTAGE,
+ cssLength);
+ }
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width")))
- style_attrs.width = a_Html_parse_length (html, attrbuf);
+ props.set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, attrbuf));
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "align"))) {
if (dStrcasecmp (attrbuf, "left") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_LEFT;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_LEFT);
else if (dStrcasecmp (attrbuf, "right") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_RIGHT;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_RIGHT);
else if (dStrcasecmp (attrbuf, "center") == 0)
- style_attrs.textAlign = dw::core::style::TEXT_ALIGN_CENTER;
+ props.set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, TEXT_ALIGN_CENTER);
}
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
- S_TOP(html)->current_bg_color = bgcolor;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- }
+ if (bgcolor != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, bgcolor);
}
+ html->styleEngine->setNonCssHints (&props);
+
+ HT2TB(html)->addParbreak (0, html->styleEngine->wordStyle ());
+
/* The style for the cells */
- cell_style = Style::create (HT2LT(html), &style_attrs);
- style_attrs = *S_TOP(html)->style;
- /* When dillo was started with the --debug-rendering option, there
- * is always a border around the cells. */
- if (dillo_dbg_rendering)
- style_attrs.borderWidth.setVal (1);
- else
- style_attrs.borderWidth.setVal (border ? 1 : 0);
- style_attrs.padding.setVal(cellpadding);
- style_attrs.setBorderColor (cell_style->borderColor.top);
- style_attrs.setBorderStyle (BORDER_INSET);
-
- old_style = S_TOP(html)->table_cell_style;
- S_TOP(html)->table_cell_style =
- Style::create (HT2LT(html), &style_attrs);
- if (old_style)
- old_style->unref ();
+ table_cell_props = new CssPropertyList ();
+ if (border > 0) {
+ cssLength = CSS_CREATE_LENGTH (1, CSS_LENGTH_TYPE_PX);
+ table_cell_props->set (CSS_PROPERTY_BORDER_TOP_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_BOTTOM_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_LEFT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_BORDER_RIGHT_WIDTH,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ }
+
+ if (cellpadding != -1) {
+ cssLength = CSS_CREATE_LENGTH (cellpadding, CSS_LENGTH_TYPE_PX);
+ table_cell_props->set (CSS_PROPERTY_PADDING_TOP,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_BOTTOM,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_LEFT,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ table_cell_props->set (CSS_PROPERTY_PADDING_RIGHT,
+ CSS_TYPE_LENGTH_PERCENTAGE, cssLength);
+ }
+
+ if (S_TOP(html)->table_cell_props)
+ S_TOP(html)->table_cell_props->unref ();
+
+ S_TOP(html)->table_cell_props = table_cell_props;
+ S_TOP(html)->table_cell_props->ref ();
table = new dw::Table(prefs.limit_text_width);
- DW2TB(html->dw)->addWidget (table, cell_style);
- cell_style->unref ();
+ HT2TB(html)->addWidget (table, html->styleEngine->style ());
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TOP;
S_TOP(html)->cell_text_align_set = FALSE;
S_TOP(html)->table = table;
-#endif
}
/*
@@ -134,12 +137,10 @@ void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize)
void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
{
const char *attrbuf;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style, *old_style;
int32_t bgcolor = -1;
bool new_style = false;
+ CssPropertyList props, *table_cell_props;
-#ifdef USE_TABLES
switch (S_TOP(html)->table_mode) {
case DILLO_HTML_TABLE_MODE_NONE:
_MSG("Invalid HTML syntax: <tr> outside <table>\n");
@@ -148,44 +149,37 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
case DILLO_HTML_TABLE_MODE_TOP:
case DILLO_HTML_TABLE_MODE_TR:
case DILLO_HTML_TABLE_MODE_TD:
- style = NULL;
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
-
- style_attrs = *S_TOP(html)->style;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- style = Style::create (HT2LT(html), &style_attrs);
- S_TOP(html)->current_bg_color = bgcolor;
- }
+ if (bgcolor != -1)
+ props.set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR, bgcolor);
}
- ((dw::Table*)S_TOP(html)->table)->addRow (style);
- if (style)
- style->unref ();
-
if (a_Html_get_attr (html, tag, tagsize, "align")) {
S_TOP(html)->cell_text_align_set = TRUE;
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ a_Html_tag_set_align_attr (html, &props, tag, tagsize);
}
- style_attrs = *S_TOP(html)->table_cell_style;
+ html->styleEngine->inheritBackgroundColor ();
+ html->styleEngine->setNonCssHints (&props);
+
+ ((dw::Table*)S_TOP(html)->table)->addRow (html->styleEngine->style ());
+
+ table_cell_props = new CssPropertyList (*S_TOP(html)->table_cell_props);
if (bgcolor != -1) {
- style_attrs.backgroundColor =Color::createShaded(HT2LT(html),bgcolor);
+ table_cell_props->set (CSS_PROPERTY_BACKGROUND_COLOR,
+ CSS_TYPE_COLOR, bgcolor);
new_style = true;
}
- if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
+ if (a_Html_tag_set_valign_attr (html, tag, tagsize, table_cell_props))
new_style = true;
if (new_style) {
- old_style = S_TOP(html)->table_cell_style;
- S_TOP(html)->table_cell_style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
+ S_TOP(html)->table_cell_props->unref ();
+ S_TOP(html)->table_cell_props = table_cell_props;
+ S_TOP(html)->table_cell_props->ref ();
+ } else {
+ delete table_cell_props;
}
break;
default:
@@ -193,9 +187,6 @@ void Html_tag_open_tr(DilloHtml *html, const char *tag, int tagsize)
}
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TR;
-#else
- DW2TB(html->dw)->addParbreak (0, S_TOP(html)->style);
-#endif
}
/*
@@ -212,13 +203,12 @@ void Html_tag_open_td(DilloHtml *html, const char *tag, int tagsize)
*/
void Html_tag_open_th(DilloHtml *html, const char *tag, int tagsize)
{
- a_Html_set_top_font(html, NULL, 0, 1, 1);
Html_tag_open_table_cell (html, tag, tagsize,
dw::core::style::TEXT_ALIGN_CENTER);
}
/*
- * Utilities
+ * Utilities
*/
/*
@@ -228,12 +218,9 @@ static void Html_tag_open_table_cell(DilloHtml *html,
const char *tag, int tagsize,
dw::core::style::TextAlignType text_align)
{
-#ifdef USE_TABLES
Widget *col_tb;
int colspan = 1, rowspan = 1;
const char *attrbuf;
- dw::core::style::StyleAttrs style_attrs;
- dw::core::style::Style *style, *old_style;
int32_t bgcolor;
bool_t new_style;
@@ -258,66 +245,53 @@ static void Html_tag_open_table_cell(DilloHtml *html,
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "rowspan")))
rowspan = MAX(1, strtol (attrbuf, NULL, 10));
+ CssPropertyList *props;
+ // \todo any shorter way to do this?
+ if (S_TOP(html)->table_cell_props != NULL)
+ props = new CssPropertyList (*S_TOP(html)->table_cell_props);
+ else
+ props = new CssPropertyList ();
+
/* text style */
- old_style = S_TOP(html)->style;
- style_attrs = *old_style;
- if (!S_TOP(html)->cell_text_align_set)
- style_attrs.textAlign = text_align;
+ if (!S_TOP(html)->cell_text_align_set) {
+ props->set (CSS_PROPERTY_TEXT_ALIGN, CSS_TYPE_ENUM, text_align);
+ }
if (a_Html_get_attr(html, tag, tagsize, "nowrap"))
- style_attrs.whiteSpace = WHITE_SPACE_NOWRAP;
+ props->set(CSS_PROPERTY_WHITE_SPACE,CSS_TYPE_ENUM,WHITE_SPACE_NOWRAP);
else
- style_attrs.whiteSpace = WHITE_SPACE_NORMAL;
-
- S_TOP(html)->style =
- Style::create (HT2LT(html), &style_attrs);
- old_style->unref ();
- a_Html_tag_set_align_attr (html, tag, tagsize);
+ props->set(CSS_PROPERTY_WHITE_SPACE,CSS_TYPE_ENUM,WHITE_SPACE_NORMAL);
- /* cell style */
- style_attrs = *S_TOP(html)->table_cell_style;
- new_style = FALSE;
+ a_Html_tag_set_align_attr (html, props, tag, tagsize);
if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "width"))) {
- style_attrs.width = a_Html_parse_length (html, attrbuf);
- new_style = TRUE;
+ props->set (CSS_PROPERTY_WIDTH, CSS_TYPE_LENGTH_PERCENTAGE,
+ a_Html_parse_length (html, attrbuf));
}
- if (a_Html_tag_set_valign_attr (html, tag, tagsize, &style_attrs))
+ if (a_Html_tag_set_valign_attr (html, tag, tagsize, props))
new_style = TRUE;
- if (!prefs.force_my_colors &&
- (attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
+ if ((attrbuf = a_Html_get_attr(html, tag, tagsize, "bgcolor"))) {
bgcolor = a_Html_color_parse(html, attrbuf, -1);
- if (bgcolor != -1) {
- if (bgcolor == 0xffffff && !prefs.allow_white_bg)
- bgcolor = prefs.bg_color;
-
- new_style = TRUE;
- style_attrs.backgroundColor =
- Color::createShaded (HT2LT(html), bgcolor);
- S_TOP(html)->current_bg_color = bgcolor;
- }
+ if (bgcolor != -1)
+ props->set (CSS_PROPERTY_BACKGROUND_COLOR, CSS_TYPE_COLOR,bgcolor);
}
- if (S_TOP(html)->style->textAlign
+ html->styleEngine->setNonCssHints (props);
+ delete props;
+
+ if (html->styleEngine->style ()->textAlign
== TEXT_ALIGN_STRING)
- col_tb = new dw::TableCell (((dw::Table*)S_TOP(html)->table)->getCellRef (),
- prefs.limit_text_width);
+ col_tb = new dw::TableCell (
+ ((dw::Table*)S_TOP(html)->table)->getCellRef (),
+ prefs.limit_text_width);
else
col_tb = new Textblock (prefs.limit_text_width);
- if (new_style) {
- style = dw::core::style::Style::create (HT2LT(html), &style_attrs);
- col_tb->setStyle (style);
- style->unref ();
- } else
- col_tb->setStyle (S_TOP(html)->table_cell_style);
+ col_tb->setStyle (html->styleEngine->style ());
((dw::Table*)S_TOP(html)->table)->addCell (col_tb, colspan, rowspan);
S_TOP(html)->textblock = html->dw = col_tb;
-
- /* Handle it when the user clicks on a link */
- html->connectSignals(col_tb);
break;
default:
@@ -326,5 +300,4 @@ static void Html_tag_open_table_cell(DilloHtml *html,
}
S_TOP(html)->table_mode = DILLO_HTML_TABLE_MODE_TD;
-#endif
}
diff --git a/src/table.hh b/src/table.hh
index 8068c184..aca717ba 100644
--- a/src/table.hh
+++ b/src/table.hh
@@ -2,13 +2,13 @@
#define __TABLE_HH__
/*
- * Classes
+ * Classes
*/
class DilloHtml;
/*
- * Table parsing functions
+ * Table parsing functions
*/
void Html_tag_open_table(DilloHtml *html, const char *tag, int tagsize);
diff --git a/src/timeout.cc b/src/timeout.cc
index 356134cc..80eb6425 100644
--- a/src/timeout.cc
+++ b/src/timeout.cc
@@ -29,7 +29,7 @@ void a_Timeout_add(float t, TimeoutCb_t cb, void *cbdata)
}
/*
- * To be called from iside the 'cb' function when it wants to keep running
+ * To be called from inside the 'cb' function when it wants to keep running
*/
void a_Timeout_repeat(float t, TimeoutCb_t cb, void *cbdata)
{
diff --git a/src/ui.cc b/src/ui.cc
index 87fab74f..fac78604 100644
--- a/src/ui.cc
+++ b/src/ui.cc
@@ -11,7 +11,7 @@
// UI for Dillo
-#include <stdlib.h>
+#include <unistd.h>
#include <stdio.h>
#include <fltk/HighlightButton.h>
@@ -25,9 +25,11 @@
#include <fltk/Item.h>
#include <fltk/Divider.h>
+#include "keys.hh"
#include "ui.hh"
#include "msg.h"
#include "timeout.hh"
+#include "utf8.hh"
using namespace fltk;
@@ -38,8 +40,9 @@ using namespace fltk;
struct iconset {
Image *ImgMeterOK, *ImgMeterBug,
- *ImgHome, *ImgReload, *ImgSave, *ImgBook, *ImgClear, *ImgSearch;
- MultiImage *ImgLeftMulti, *ImgRightMulti, *ImgStopMulti, *ImgImageLoadMulti;
+ *ImgHome, *ImgReload, *ImgSave, *ImgBook, *ImgTools,
+ *ImgClear,*ImgSearch, *ImgHelp;
+ MultiImage *ImgLeftMulti, *ImgRightMulti, *ImgStopMulti;
};
static struct iconset standard_icons = {
@@ -49,16 +52,16 @@ static struct iconset standard_icons = {
new xpmImage(reload_xpm),
new xpmImage(save_xpm),
new xpmImage(bm_xpm),
+ new xpmImage(tools_xpm),
new xpmImage(new_s_xpm),
new xpmImage(search_xpm),
+ new xpmImage(help_xpm),
new MultiImage(*new xpmImage(left_xpm), INACTIVE_R,
*new xpmImage(left_i_xpm)),
new MultiImage(*new xpmImage(right_xpm), INACTIVE_R,
*new xpmImage(right_i_xpm)),
new MultiImage(*new xpmImage(stop_xpm), INACTIVE_R,
*new xpmImage(stop_i_xpm)),
- new MultiImage(*new xpmImage(imgload_off_xpm), STATE,
- *new xpmImage(imgload_on_xpm))
};
static struct iconset small_icons = {
@@ -68,15 +71,16 @@ static struct iconset small_icons = {
new xpmImage(reload_s_xpm),
new xpmImage(save_s_xpm),
new xpmImage(bm_s_xpm),
+ new xpmImage(tools_s_xpm),
new xpmImage(new_s_xpm),
standard_icons.ImgSearch,
+ standard_icons.ImgHelp,
new MultiImage(*new xpmImage(left_s_xpm), INACTIVE_R,
*new xpmImage(left_si_xpm)),
new MultiImage(*new xpmImage(right_s_xpm), INACTIVE_R,
*new xpmImage(right_si_xpm)),
new MultiImage(*new xpmImage(stop_s_xpm), INACTIVE_R,
*new xpmImage(stop_si_xpm)),
- standard_icons.ImgImageLoadMulti
};
@@ -99,7 +103,7 @@ public:
};
/*
- * Disable: UpKey, DownKey, PageUpKey, PageDownKey and
+ * Disable: UpKey, DownKey, PageUpKey, PageDownKey and
* CTRL+{o,r,HomeKey,EndKey}
*/
int CustInput::handle(int e)
@@ -167,11 +171,11 @@ int CustHighlightButton::handle(int e)
* Used to resize the progress boxes automatically.
*/
class CustProgressBox : public InvisibleBox {
+ int padding;
public:
CustProgressBox(int x, int y, int w, int h, const char *l=0) :
- InvisibleBox(x,y,w,h,l) {};
+ InvisibleBox(x,y,w,h,l) { padding = 0; };
void update_label(const char *lbl) {
- static int padding = 0;
int w,h;
if (!padding) {
copy_label("W");
@@ -189,7 +193,7 @@ public:
// Toolbar buttons -----------------------------------------------------------
//
//static const char *button_names[] = {
-// "Back", "Forward", "Home", "Reload", "Save", "Stop", "Bookmarks",
+// "Back", "Forward", "Home", "Reload", "Save", "Stop", "Bookmarks", "Tools",
// "Clear", "Search"
//};
@@ -215,6 +219,26 @@ static void search_cb(Widget *wid, void *data)
}
/*
+ * Callback for the help button.
+ */
+static void help_cb(Widget *w, void *)
+{
+ char *path = dStrconcat(DILLO_DOCDIR, "user_help.html", NULL);
+ BrowserWindow *bw = a_UIcmd_get_bw_by_widget(w);
+
+ if (access(path, R_OK) == 0) {
+ char *urlstr = dStrconcat("file:", path, NULL);
+ a_UIcmd_open_urlstr(bw, urlstr);
+ dFree(urlstr);
+ } else {
+ MSG("Can't read local help file at \"%s\"."
+ " Getting remote help...\n", path);
+ a_UIcmd_open_urlstr(bw, "http://www.dillo.org/dillo2-help.html");
+ }
+ dFree(path);
+}
+
+/*
* Callback for the File menu button.
*/
static void filemenu_cb(Widget *wid, void *)
@@ -324,6 +348,11 @@ static void b1_cb(Widget *wid, void *cb_data)
a_UIcmd_book(a_UIcmd_get_bw_by_widget(wid));
}
break;
+ case UI_TOOLS:
+ if (k == 1 || k == 3) {
+ a_UIcmd_tools(a_UIcmd_get_bw_by_widget(wid), wid);
+ }
+ break;
default:
break;
}
@@ -370,50 +399,58 @@ PackedGroup *UI::make_toolbar(int tw, int th)
p1->begin();
Back = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Back" : 0);
b->image(icons->ImgLeftMulti);
- b->tooltip("Previous page");
b->callback(b1_cb, (void *)UI_BACK);
b->clear_tab_to_focus();
HighlightButton::default_style->highlight_color(CuteColor);
Forw = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Forw" : 0);
b->image(icons->ImgRightMulti);
- b->tooltip("Next page");
b->callback(b1_cb, (void *)UI_FORW);
b->clear_tab_to_focus();
-
+
Home = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Home" : 0);
b->image(icons->ImgHome);
- b->tooltip("Go to the Home page");
b->callback(b1_cb, (void *)UI_HOME);
b->clear_tab_to_focus();
Reload = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Reload" : 0);
b->image(icons->ImgReload);
- b->tooltip("Reload");
b->callback(b1_cb, (void *)UI_RELOAD);
b->clear_tab_to_focus();
-
+
Save = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Save" : 0);
b->image(icons->ImgSave);
- b->tooltip("Save this page");
b->callback(b1_cb, (void *)UI_SAVE);
b->clear_tab_to_focus();
-
+
Stop = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Stop" : 0);
b->image(icons->ImgStopMulti);
- b->tooltip("Stop loading");
b->callback(b1_cb, (void *)UI_STOP);
b->clear_tab_to_focus();
Bookmarks = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Book" : 0);
b->image(icons->ImgBook);
- b->tooltip("View bookmarks");
b->callback(b1_cb, (void *)UI_BOOK);
b->clear_tab_to_focus();
+ Tools = b = new HighlightButton(xpos, 0, bw, bh, (lbl) ? "Tools" : 0);
+ b->image(icons->ImgTools);
+ b->callback(b1_cb, (void *)UI_TOOLS);
+ b->clear_tab_to_focus();
+
p1->type(PackedGroup::ALL_CHILDREN_VERTICAL);
p1->end();
+ if (prefs.show_tooltip) {
+ Back->tooltip("Previous page");
+ Forw->tooltip("Next page");
+ Home->tooltip("Go to the Home page");
+ Reload->tooltip("Reload");
+ Save->tooltip("Save this page");
+ Stop->tooltip("Stop loading");
+ Bookmarks->tooltip("View bookmarks");
+ Tools->tooltip("Settings");
+ }
return p1;
}
@@ -427,12 +464,10 @@ PackedGroup *UI::make_location()
pg->begin();
Clear = b = new CustHighlightButton(2,2,16,22,0);
b->image(icons->ImgClear);
- b->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
b->callback(clear_cb, this);
b->clear_tab_to_focus();
Input *i = Location = new CustInput(0,0,0,0,0);
- i->tooltip("Location");
i->color(CuteColor);
i->when(WHEN_ENTER_KEY);
i->callback(location_cb, this);
@@ -440,14 +475,24 @@ PackedGroup *UI::make_location()
Search = b = new HighlightButton(0,0,16,22,0);
b->image(icons->ImgSearch);
- b->tooltip("Search the Web");
b->callback(search_cb, this);
b->clear_tab_to_focus();
+ Help = b = new HighlightButton(0,0,16,22,0);
+ b->image(icons->ImgHelp);
+ b->callback(help_cb, this);
+ b->clear_tab_to_focus();
+
pg->type(PackedGroup::ALL_CHILDREN_VERTICAL);
pg->resizable(i);
pg->end();
+ if (prefs.show_tooltip) {
+ Clear->tooltip("Clear the URL box.\nMiddle-click to paste a URL.");
+ Location->tooltip("Location");
+ Search->tooltip("Search the Web");
+ Help->tooltip("Help");
+ }
return pg;
}
@@ -494,7 +539,8 @@ Widget *UI::make_filemenu_button()
_MSG("UI::make_filemenu_button w=%d h=%d padding=%d\n", w, h, padding);
btn->box(PanelSize == P_large ? FLAT_BOX : THIN_UP_BOX);
btn->callback(filemenu_cb, this);
- btn->tooltip("File menu");
+ if (prefs.show_tooltip)
+ btn->tooltip("File menu");
btn->clear_tab_to_focus();
if (!prefs.show_filemenu && PanelSize != P_large)
btn->hide();
@@ -520,7 +566,7 @@ Group *UI::make_panel(int ww)
icons = &small_icons;
else
icons = &standard_icons;
-
+
if (PanelSize == P_tiny) {
if (Small_Icons)
xpos = 0, bw = 22, bh = 22, fh = 0, lh = 22, lbl = 0;
@@ -584,7 +630,7 @@ Group *UI::make_panel(int ww)
g2->resizable(pg);
g2->end();
-
+
// Toolbar
g3 = new Group(0,fh+lh,ww,bh);
g3->begin();
@@ -600,12 +646,12 @@ Group *UI::make_panel(int ww)
w = make_progress_bars(1,0);
}
pg->add(w);
-
+
g3->resizable(pg); // Better than 'w3' and it also works
pg->box(BORDER_FRAME);
//g3->box(EMBOSSED_BOX);
g3->end();
-
+
g1->resizable(g3);
g1->end();
}
@@ -614,16 +660,45 @@ Group *UI::make_panel(int ww)
}
/*
+ * Create the status panel
+ */
+Group *UI::make_status_panel(int ww)
+{
+ const int s_h = 20, bm_w = 16;
+ Group *g = new Group(0, 0, ww, s_h, 0);
+
+ // Status box
+ Status = new Output(0, 0, ww-bm_w, s_h, 0);
+ Status->value("");
+ Status->box(THIN_DOWN_BOX);
+ Status->clear_click_to_focus();
+ Status->clear_tab_to_focus();
+ Status->color(GRAY80);
+ g->add(Status);
+ //Status->throw_focus();
+
+ // Bug Meter
+ BugMeter = new HighlightButton(ww-bm_w,0,bm_w,s_h,0);
+ BugMeter->image(icons->ImgMeterOK);
+ BugMeter->box(THIN_DOWN_BOX);
+ BugMeter->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
+ if (prefs.show_tooltip)
+ BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
+ BugMeter->callback(bugmeter_cb, this);
+ BugMeter->clear_tab_to_focus();
+ g->add(BugMeter);
+
+ g->resizable(Status);
+ return g;
+}
+
+/*
* User Interface constructor
- */
+ */
UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Group(x, y, ww, wh, label)
{
- int s_h = 20;
-
- Font *f = font(prefs.vw_fontname, 0);
- if (f)
- this->labelfont(f);
+ PointerOnLink = FALSE;
Tabs = NULL;
TabTooltip = NULL;
@@ -631,12 +706,16 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
add(TopGroup);
resizable(TopGroup);
set_flag(RAW_LABEL);
-
+
if (cur_ui) {
PanelSize = cur_ui->PanelSize;
CuteColor = cur_ui->CuteColor;
Small_Icons = cur_ui->Small_Icons;
- Panelmode = cur_ui->Panelmode;
+ if (cur_ui->Panelmode == UI_HIDDEN ||
+ cur_ui->Panelmode == UI_TEMPORARILY_SHOW_PANELS)
+ Panelmode = UI_HIDDEN;
+ else
+ Panelmode = UI_NORMAL;
} else {
// Set some default values
//PanelSize = P_tiny, CuteColor = 26, Small_Icons = 0;
@@ -646,12 +725,10 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Panelmode = (UIPanelmode) prefs.fullwindow_start;
}
-
// Control panel
Panel = make_panel(ww);
TopGroup->add(Panel);
-
// Render area
Main = new Widget(0,0,1,1,"Welcome...");
Main->box(FLAT_BOX);
@@ -666,47 +743,11 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
// Find text bar
findbar = new Findbar(ww, 28);
- TopGroup->add(findbar);
+ TopGroup->add(findbar);
// Status Panel
- StatusPanel = new Group(0, 0, ww, s_h, 0);
- // Status box
- int il_w = 16;
- int bm_w = 16;
- Status = new Output(0, 0, ww-bm_w-il_w, s_h, 0);
- Status->value("");
- Status->box(THIN_DOWN_BOX);
- Status->clear_click_to_focus();
- Status->clear_tab_to_focus();
- Status->color(GRAY80);
- StatusPanel->add(Status);
- //Status->throw_focus();
-
- // Image loading indicator
- ImageLoad = new HighlightButton(ww-il_w-bm_w,0,il_w,s_h,0);
- ImageLoad->type(Button::TOGGLE);
- ImageLoad->state(prefs.load_images);
- ImageLoad->image(icons->ImgImageLoadMulti);
-
- ImageLoad->box(THIN_DOWN_BOX);
- ImageLoad->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
- ImageLoad->tooltip("Toggle image loading");
- ImageLoad->clear_tab_to_focus();
- StatusPanel->add(ImageLoad);
-
- // Bug Meter
- BugMeter = new HighlightButton(ww-bm_w,0,bm_w,s_h,0);
- BugMeter->image(icons->ImgMeterOK);
- BugMeter->box(THIN_DOWN_BOX);
- BugMeter->align(ALIGN_INSIDE|ALIGN_CLIP|ALIGN_LEFT);
- BugMeter->tooltip("Show HTML bugs\n(right-click for menu)");
- BugMeter->callback(bugmeter_cb, this);
- BugMeter->clear_tab_to_focus();
- StatusPanel->add(BugMeter);
-
- StatusPanel->resizable(Status);
-
- TopGroup->add(StatusPanel);
+ StatusPanel = make_status_panel(ww);
+ TopGroup->add(StatusPanel);
// Make the full screen button (to be attached to the viewport later)
// TODO: attach to the viewport
@@ -721,8 +762,6 @@ UI::UI(int x, int y, int ww, int wh, const char* label, const UI *cur_ui) :
Panel->hide();
StatusPanel->hide();
}
-
- //show();
}
/*
@@ -742,69 +781,102 @@ int UI::handle(int event)
_MSG("UI::handle event=%d (%d,%d)\n", event, event_x(), event_y());
_MSG("Panel->h()=%d Main->h()=%d\n", Panel->h() , Main->h());
- int ret = 0, k = event_key();
-
- // We're only interested in some flags
- unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
+ int ret = 0;
if (event == KEY) {
return 0; // Receive as shortcut
-
} else if (event == SHORTCUT) {
- // Handle keyboard shortcuts here.
- if (modifier == CTRL) {
- if (k == 'b') {
- a_UIcmd_book(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'f') {
- set_findbar_visibility(1);
- ret = 1;
- } else if (k == 'l') {
- focus_location();
- ret = 1;
- } else if (k == 'n') {
- a_UIcmd_browser_window_new(w(),h(),a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'o') {
- a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'q') {
- a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 'r') {
- a_UIcmd_reload(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 's') {
- a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if (k == 't') {
- a_UIcmd_open_url_nt(a_UIcmd_get_bw_by_widget(this), NULL, 1);
- ret = 1;
- } else if (k == ' ') {
- panelmode_cb_i();
- ret = 1;
- }
- } else if (modifier == ALT) {
- if (k == 'f') {
- a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
- } else if (k == 'q' && event_key_state(LeftAltKey)) {
- a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
+ KeysCommand_t cmd = Keys::getKeyCmd();
+ if (cmd == KEYS_NOP) {
+ // Do nothing
+ } else if (cmd == KEYS_SCREEN_UP || cmd == KEYS_SCREEN_DOWN ||
+ cmd == KEYS_LINE_UP || cmd == KEYS_LINE_DOWN ||
+ cmd == KEYS_LEFT || cmd == KEYS_RIGHT ||
+ cmd == KEYS_TOP || cmd == KEYS_BOTTOM) {
+ a_UIcmd_scroll(a_UIcmd_get_bw_by_widget(this), cmd);
+ ret = 1;
+ } else if (cmd == KEYS_BACK) {
+ a_UIcmd_back(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FORWARD) {
+ a_UIcmd_forw(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_BOOKMARKS) {
+ a_UIcmd_book(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FIND) {
+ set_findbar_visibility(1);
+ ret = 1;
+ } else if (cmd == KEYS_WEBSEARCH) {
+ a_UIcmd_search_dialog(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_GOTO) {
+ focus_location();
+ ret = 1;
+ } else if (cmd == KEYS_NEW_TAB) {
+ a_UIcmd_open_url_nt(a_UIcmd_get_bw_by_widget(this), NULL, 1);
+ ret = 1;
+ } else if (cmd == KEYS_CLOSE_TAB) {
+ a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_HIDE_PANELS &&
+ get_panelmode() == UI_TEMPORARILY_SHOW_PANELS) {
+ set_panelmode(UI_HIDDEN);
+ ret = 1;
+ } else if (cmd == KEYS_NEW_WINDOW) {
+ a_UIcmd_browser_window_new(w(),h(),0,a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_OPEN) {
+ a_UIcmd_open_file(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_HOME) {
+ a_UIcmd_home(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_RELOAD) {
+ a_UIcmd_reload(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_STOP) {
+ a_UIcmd_stop(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_SAVE) {
+ a_UIcmd_save(a_UIcmd_get_bw_by_widget(this));
+ ret = 1;
+ } else if (cmd == KEYS_FULLSCREEN) {
+ panelmode_cb_i();
+ ret = 1;
+ } else if (cmd == KEYS_FILE_MENU) {
+ a_UIcmd_file_popup(a_UIcmd_get_bw_by_widget(this), FileButton);
+ ret = 1;
+ } else if (cmd == KEYS_CLOSE_ALL) {
+ a_Timeout_add(0.0, a_UIcmd_close_all_bw, NULL);
+ ret = 1;
+ }
+ } else if (event == PUSH) {
+ if (prefs.middle_click_drags_page == 0 &&
+ event_button() == MiddleButton &&
+ !a_UIcmd_pointer_on_link(a_UIcmd_get_bw_by_widget(this))) {
+ if (Main->Rectangle::contains (event_x (), event_y ())) {
+ /* Offer the event to Main's children (form widgets) */
+ int save_x = e_x, save_y = e_y;
+
+ e_x -= Main->x();
+ e_y -= Main->y();
+ ret = ((Group *)Main)->Group::handle(event);
+ e_x = save_x;
+ e_y = save_y;
}
- } else {
- // Back and Forward navigation shortcuts
- if (modifier == 0 && (k == BackSpaceKey || k == ',')) {
- a_UIcmd_back(a_UIcmd_get_bw_by_widget(this));
- ret = 1;
- } else if ((modifier == 0 && k == '.') ||
- (modifier == SHIFT && k == BackSpaceKey)) {
- a_UIcmd_forw(a_UIcmd_get_bw_by_widget(this));
+ if (!ret) {
+ /* middle click was not on a link or a form widget */
+ paste_url();
ret = 1;
}
}
}
- if (!ret)
+ if (!ret) {
ret = Group::handle(event);
+ }
+
return ret;
}
@@ -898,7 +970,7 @@ void UI::set_img_prog(int n_img, int t_img, int cmd)
} else {
IProg->activate();
if (cmd == 1) {
- snprintf(str, 32, "%s%d of %d",
+ snprintf(str, 32, "%s%d of %d",
(PanelSize == 0) ? "" : "Images\n", n_img, t_img);
} else if (cmd == 2) {
str[0] = '\0';
@@ -926,9 +998,7 @@ void UI::set_bug_prog(int n_bug)
BugMeter->redraw_label();
new_w = strlen(str)*8 + 20;
}
- Status->resize(0,0,StatusPanel->w()-ImageLoad->w()-new_w,Status->h());
- ImageLoad->resize(StatusPanel->w()-ImageLoad->w()-new_w, 0, ImageLoad->w(),
- ImageLoad->h());
+ Status->resize(0,0,StatusPanel->w()-new_w,Status->h());
BugMeter->resize(StatusPanel->w()-new_w, 0, new_w, BugMeter->h());
StatusPanel->init_sizes();
}
@@ -954,12 +1024,16 @@ void UI::customize(int flags)
Stop->hide();
if ( !prefs.show_bookmarks )
Bookmarks->hide();
+ if ( !prefs.show_tools )
+ Tools->hide();
if ( !prefs.show_clear_url )
Clear->hide();
if ( !prefs.show_url )
Location->hide();
if ( !prefs.show_search )
Search->hide();
+ if ( !prefs.show_help )
+ Help->hide();
if ( !prefs.show_progress_box )
ProgBox->hide();
}
@@ -987,7 +1061,8 @@ void UI::panel_cb_i()
*/
void UI::color_change_cb_i()
{
- static int ncolor = 0, cols[] = {7,17,26,51,140,156,205,206,215,-1};
+ const int cols[] = {7,17,26,51,140,156,205,206,215,-1};
+ static int ncolor = 0;
ncolor = (cols[ncolor+1] < 0) ? 0 : ncolor + 1;
CuteColor = cols[ncolor];
@@ -1048,32 +1123,28 @@ void UI::set_render_layout(Widget &nw)
}
/*
- * Set the window title
+ * Set the tab title
*/
-void UI::set_page_title(const char *label)
+void UI::set_tab_title(const char *label)
{
char title[128];
dReturn_if_fail(label != NULL);
- snprintf(title, 128, "Dillo: %s", label);
- this->window()->copy_label(title);
- this->window()->redraw_label();
+ if (*label) {
+ // Make a label for this tab
+ size_t tab_chars = 18, label_len = strlen(label);
- if (tabs() && *label) {
- size_t tab_chars = 18;
+ if (label_len > tab_chars)
+ tab_chars = a_Utf8_end_of_char(label, tab_chars - 1) + 1;
snprintf(title, tab_chars + 1, "%s", label);
- if (strlen(label) > tab_chars) {
- while (label[tab_chars] & 0x80 && !(label[tab_chars] & 0x40) &&
- tab_chars < 23) {
- // In the middle of a multibyte UTF-8 character.
- title[tab_chars] = label[tab_chars];
- tab_chars++;
- }
+ if (label_len > tab_chars)
snprintf(title + tab_chars, 4, "...");
+ // Avoid unnecessary redraws
+ if (strcmp(this->label(), title)) {
+ this->copy_label(title);
+ this->redraw_label();
}
- this->copy_label(title);
- this->redraw_label();
// Disabled because of a bug in fltk::Tabgroup
//dFree(TabTooltip);
diff --git a/src/ui.hh b/src/ui.hh
index 7a302efd..9e1b6d4f 100644
--- a/src/ui.hh
+++ b/src/ui.hh
@@ -16,8 +16,6 @@
#include "findbar.hh"
-using namespace fltk;
-
typedef enum {
UI_BACK = 0,
UI_FORW,
@@ -26,6 +24,7 @@ typedef enum {
UI_SAVE,
UI_STOP,
UI_BOOK,
+ UI_TOOLS,
UI_CLEAR,
UI_SEARCH
} UIButton;
@@ -47,15 +46,15 @@ class UI : public fltk::Group {
CustTabGroup *Tabs;
char *TabTooltip;
- Group *TopGroup;
- Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks,
- *Clear, *Search, *FullScreen, *ImageLoad, *BugMeter, *FileButton;
- Input *Location;
- PackedGroup *ProgBox;
+ fltk::Group *TopGroup;
+ fltk::Button *Back, *Forw, *Home, *Reload, *Save, *Stop, *Bookmarks, *Tools,
+ *Clear, *Search, *Help, *FullScreen, *BugMeter, *FileButton;
+ fltk::Input *Location;
+ fltk::PackedGroup *ProgBox;
CustProgressBox *PProg, *IProg;
- Group *Panel, *StatusPanel;
- Widget *Main;
- Output *Status;
+ fltk::Group *Panel, *StatusPanel;
+ fltk::Widget *Main;
+ fltk::Output *Status;
int MainIdx;
// Panel customization variables
@@ -64,13 +63,15 @@ class UI : public fltk::Group {
UIPanelmode Panelmode;
Findbar *findbar;
+ int PointerOnLink;
- PackedGroup *make_toolbar(int tw, int th);
- PackedGroup *make_location();
- PackedGroup *make_progress_bars(int wide, int thin_up);
+ fltk::PackedGroup *make_toolbar(int tw, int th);
+ fltk::PackedGroup *make_location();
+ fltk::PackedGroup *make_progress_bars(int wide, int thin_up);
void make_menubar(int x, int y, int w, int h);
- Widget *make_filemenu_button();
- Group *make_panel(int ww);
+ fltk::Widget *make_filemenu_button();
+ fltk::Group *make_panel(int ww);
+ fltk::Group *make_status_panel(int ww);
public:
@@ -89,20 +90,20 @@ public:
void set_img_prog(int n_img, int t_img, int cmd);
void set_bug_prog(int n_bug);
void set_render_layout(Widget &nw);
- void set_page_title(const char *label);
+ void set_tab_title(const char *label);
void customize(int flags);
void button_set_sens(UIButton btn, int sens);
void paste_url();
void set_panelmode(UIPanelmode mode);
UIPanelmode get_panelmode();
void set_findbar_visibility(bool visible);
- bool images_enabled() { return ImageLoad->state();}
- void images_enabled(int flag) { ImageLoad->state(flag);}
Widget *fullscreen_button() { return FullScreen; }
void fullscreen_toggle() { FullScreen->do_callback(); }
CustTabGroup *tabs() { return Tabs; }
void tabs(CustTabGroup *tabs) { Tabs = tabs; }
+ int pointerOnLink() { return PointerOnLink; }
+ void pointerOnLink(int flag) { PointerOnLink = flag; }
// Hooks to method callbacks
void panel_cb_i();
diff --git a/src/uicmd.cc b/src/uicmd.cc
index 564e9957..93836735 100644
--- a/src/uicmd.cc
+++ b/src/uicmd.cc
@@ -15,15 +15,22 @@
#include <stdio.h>
#include <stdarg.h>
#include <math.h> /* for rint */
+
+#include <fltk/draw.h>
+#include <fltk/damage.h>
#include <fltk/Widget.h>
#include <fltk/TabGroup.h>
+#include <fltk/Tooltip.h>
-#include "dir.h"
+#include "paths.hh"
+#include "keys.hh"
#include "ui.hh"
#include "uicmd.hh"
#include "timeout.hh"
+#include "utf8.hh"
#include "menu.hh"
#include "dialog.hh"
+#include "xembed.hh"
#include "bookmark.h"
#include "history.h"
#include "msg.h"
@@ -33,6 +40,8 @@
#include "nav.h"
+#define DEFAULT_TAB_LABEL "Dillo"
+
// Handy macro
#define BW2UI(bw) ((UI*)((bw)->ui))
@@ -49,20 +58,198 @@ static char *save_dir = NULL;
using namespace fltk;
-//
-// For custom handling of keyboard
-//
+
+//----------------------------------------------------------------------------
+#define BTN_W 25
+#define BTN_H 20
+
+static int btn_x;
+
+/*
+ * Adds a tab-close button at the rightmost part
+ */
+class CustShrinkTabPager : public TabGroupPager {
+ bool btn_hl;
+ TabGroup *tg;
+public:
+ int update_positions(
+ TabGroup *g, int numchildren, int &selected,
+ int &cumulated_width, int &available_width,
+ int *tab_pos, int *tab_width);
+ virtual int which(TabGroup* g, int m_x,int m_y);
+ virtual TabGroupPager* clone() const;
+ virtual const char * mode_name() const {return "Shrink";}
+ virtual int id() const {return PAGER_SHRINK;}
+ virtual int available_width(TabGroup *g) const;
+ virtual bool draw_tabs(TabGroup* g, int selected, int* tab_pos,
+ int* tab_width) {
+ if (!tg) tg = g;
+ if (g->children() > 1) {
+ fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
+ setcolor(btn_hl ? 206 : GRAY75);
+ fillrect(r);
+ if (btn_hl) {
+ setcolor(WHITE);
+ strokerect(r);
+ }
+ setcolor(GRAY10);
+ //fltk::setfont(fltk::getfont()->bold(), fltk::getsize());
+ r.h(r.h()-2);
+ drawtext("X", r, ALIGN_CENTER);
+ return false;
+ } else {
+ // WORKAROUND: for http://fltk.org/str.php?L2062
+ // By returning true we avoid a call to TabGroup::draw_tab()
+ // in TabGroup::draw() in case we don't show the tabs.
+ return true;
+ }
+ }
+
+ void btn_highlight(bool flag) {
+ if (btn_hl != flag) {
+ btn_hl = flag;
+ if (tg)
+ tg->redraw(DAMAGE_VALUE);
+ }
+ };
+ bool btn_highlight() { return btn_hl; };
+
+ CustShrinkTabPager() : TabGroupPager() {
+ noclip(true);
+ btn_hl = false;
+ tg = NULL;
+ }
+};
+
+int CustShrinkTabPager::available_width(TabGroup *g) const
+{
+ _MSG("CustShrinkTabPager::available_width\n");
+ int w = MAX (g->w() - this->slope()-1 - BTN_W, 0);
+ btn_x = w + 6;
+ return w;
+}
+
+int CustShrinkTabPager::which(TabGroup* g, int event_x,int event_y)
+{
+ int H = g->tab_height();
+ if (!H) return -1;
+ if (H < 0) {
+ if (event_y > g->h() || event_y < g->h()+H) return -1;
+ } else {
+ if (event_y > H || event_y < 0) return -1;
+ }
+ if (event_x < 0) return -1;
+ int p[128], w[128];
+ int selected = g->tab_positions(p, w);
+ int d = (event_y-(H>=0?0:g->h()))*slope()/H;
+ for (int i=0; i<g->children(); i++) {
+ if (event_x < p[i+1]+(i<selected ? slope() - d : d)) return i;
+ }
+ return -1;
+}
+
+/*
+ * Prevents tabs from going over the close-tab button.
+ * Modified from fltk-2.0.x-r6525.
+ */
+int CustShrinkTabPager::update_positions(
+ TabGroup *g, int numchildren, int &selected,
+ int &cumulated_width, int &available_width,
+ int *tab_pos, int *tab_width)
+{
+ available_width-=BTN_W;
+
+ // uh oh, they are too big, we must move them:
+ // special case when the selected tab itself is too big, make it fill
+ // cumulated_width:
+ int i;
+
+ if (tab_width[selected] >= available_width) {
+ tab_width[selected] = available_width;
+ for (i = 0; i <= selected; i++)
+ tab_pos[i] = 0;
+ for (i = selected + 1; i <= numchildren; i++)
+ tab_pos[i] = available_width;
+ return selected;
+ }
+
+ int w2[128];
+
+ for (i = 0; i < numchildren; i++)
+ w2[i] = tab_width[i];
+ i = numchildren - 1;
+ int j = 0;
+
+ int minsize = 5;
+
+ bool right = true;
+
+ while (cumulated_width > available_width) {
+ int n; // which one to shrink
+
+ if (j < selected && (!right || i <= selected)) { // shrink a left one
+ n = j++;
+ right = true;
+ } else if (i > selected) { // shrink a right one
+ n = i--;
+ right = false;
+ } else { // no more space, start making them zero
+ minsize = 0;
+ i = numchildren - 1;
+ j = 0;
+ right = true;
+ continue;
+ }
+ cumulated_width -= w2[n] - minsize;
+ w2[n] = minsize;
+ if (cumulated_width < available_width) {
+ w2[n] = available_width - cumulated_width + minsize;
+ cumulated_width = available_width;
+ break;
+ }
+ }
+ // re-sum the positions:
+ cumulated_width = 0;
+ for (i = 0; i < numchildren; i++) {
+ cumulated_width += w2[i];
+ tab_pos[i+1] = cumulated_width;
+ }
+ return selected;
+}
+
+TabGroupPager* CustShrinkTabPager::clone() const {
+ return new CustShrinkTabPager(*this);
+}
+
+//----------------------------------------------------------------------------
+
+/*
+ * For custom handling of keyboard
+ */
class CustTabGroup : public fltk::TabGroup {
+ Tooltip *toolTip;
+ bool tooltipEnabled;
+ bool buttonPushed;
public:
CustTabGroup (int x, int y, int ww, int wh, const char *lbl=0) :
- TabGroup(x,y,ww,wh,lbl) {};
+ TabGroup(x,y,ww,wh,lbl) {
+ // The parameter pager is cloned, so free it.
+ CustShrinkTabPager *cp = new CustShrinkTabPager();
+ this->pager(cp);
+ delete cp;
+ toolTip = new Tooltip;
+ tooltipEnabled = false;
+ buttonPushed = false;
+ };
+ ~CustTabGroup() { delete toolTip; }
int handle(int e) {
// Don't focus with arrow keys
_MSG("CustTabGroup::handle %d\n", e);
- int k = event_key();
- // We're only interested in some flags
- unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
+ fltk::Rectangle r(btn_x,0,BTN_W,BTN_H);
if (e == KEY) {
+ int k = event_key();
+ // We're only interested in some flags
+ unsigned modifier = event_state() & (SHIFT | CTRL | ALT);
if (k == UpKey || k == DownKey || k == TabKey) {
return 0;
} else if (k == LeftKey || k == RightKey) {
@@ -74,20 +261,77 @@ public:
return 1;
}
// Avoid focus change.
- return 0;
+ return 0;
+ }
+ } else if (e == FOCUS_CHANGE) {
+ // Update the window title
+ BrowserWindow *bw = a_UIcmd_get_bw_by_widget(selected_child());
+ const char *title = a_History_get_title(NAV_TOP_UIDX(bw), 1);
+ a_UIcmd_set_page_title(bw, title ? title : "");
+ } else if (e == MOVE) {
+ CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
+ if (event_inside(r) && children() > 1) {
+ /* We're inside the button area */
+ cstp->btn_highlight(true);
+ if (prefs.show_tooltip) {
+ /* Prepare the tooltip for pop-up */
+ tooltipEnabled = true;
+ /* We use parent() if available because we are returning 0.
+ * Returning without having TabGroup processing makes the
+ * popup event never reach 'this', but it reaches parent() */
+ toolTip->enter(parent() ?parent():this, r, "Close current Tab");
+ }
+ return 0; // Change focus
+ } else {
+ cstp->btn_highlight(false);
+
+ if (prefs.show_tooltip) {
+ /* Hide the tooltip or enable it again.*/
+ if (tooltipEnabled) {
+ tooltipEnabled = false;
+ toolTip->exit();
+ } else {
+ toolTip->enable();
+ }
+ }
+ }
+ } else if (e == PUSH && event_inside(r) &&
+ event_button() == 1 && children() > 1) {
+ buttonPushed = true;
+ return 1; /* non-zero */
+ } else if (e == RELEASE) {
+ if (event_inside(r) && event_button() == 1 &&
+ children() > 1 && buttonPushed) {
+ a_UIcmd_close_bw(a_UIcmd_get_bw_by_widget(selected_child()));
+ } else {
+ CustShrinkTabPager *cstp = (CustShrinkTabPager *) pager();
+ cstp->btn_highlight(false);
}
+ buttonPushed = false;
+ } else if (e == DRAG) {
+ /* Ignore this event */
+ return 1;
}
- return TabGroup::handle(e);
+ int ret = TabGroup::handle(e);
+
+ if (e == PUSH) {
+ /* WORKAROUND: FLTK raises the window on unhandled clicks,
+ * which we do not want.
+ */
+ ret = 1;
+ }
+ return ret;
}
void remove (Widget *w) {
TabGroup::remove (w);
/* fixup resizable in case we just removed it */
- if (resizable () == w)
+ if (resizable () == w) {
if (children () > 0)
resizable (child (children () - 1));
else
resizable (NULL);
+ }
if (children () < 2)
hideLabels ();
@@ -110,11 +354,12 @@ public:
}
};
+//----------------------------------------------------------------------------
+
static void win_cb (fltk::Widget *w, void *cb_data) {
- CustTabGroup *tabs;
int choice = 0;
+ CustTabGroup *tabs = (CustTabGroup*) cb_data;
- tabs = BW2UI((BrowserWindow*) cb_data)->tabs();
if (tabs->children () > 1)
choice = a_Dialog_choice3 ("Window contains more than one tab.",
"Close all tabs", "Cancel", NULL);
@@ -155,10 +400,12 @@ void a_UIcmd_send_event_to_tabs_by_wid(int e, void *v_wid)
* Create a new UI and its associated BrowserWindow data structure.
* Use style from v_ui. If non-NULL it must be of type UI*.
*/
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
+ uint32_t xid, const void *vbw)
{
BrowserWindow *old_bw = (BrowserWindow*)vbw;
BrowserWindow *new_bw = NULL;
+ Window *win;
if (ww <= 0 || wh <= 0) {
// Set default geometry from dillorc.
@@ -166,7 +413,11 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
wh = prefs.height;
}
- Window *win = new Window(ww, wh);
+ if (xid)
+ win = new Xembed(xid, ww, wh);
+ else
+ win = new Window(ww, wh);
+
win->shortcut(0); // Ignore Escape
if (prefs.buffered_drawing != 2)
win->clear_double_buffer();
@@ -177,7 +428,8 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
win->add(DilloTabs);
// Create and set the UI
- UI *new_ui = new UI(0, 0, ww, wh, "Label", old_bw ? BW2UI(old_bw) : NULL);
+ UI *new_ui = new UI(0, 0, ww, wh, DEFAULT_TAB_LABEL,
+ old_bw ? BW2UI(old_bw) : NULL);
new_ui->set_status("http://www.dillo.org/");
new_ui->tabs(DilloTabs);
@@ -203,7 +455,7 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
viewport->setBufferedDrawing (true);
else
viewport->setBufferedDrawing (false);
-
+
layout->attachView (viewport);
new_ui->set_render_layout(*viewport);
@@ -217,7 +469,9 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
// Copy the layout pointer into the bw data
new_bw->render_layout = (void*)layout;
- win->callback(win_cb, new_bw);
+ win->callback(win_cb, DilloTabs);
+
+ new_ui->focus_location();
return new_bw;
}
@@ -226,7 +480,7 @@ BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *vbw)
* Create a new Tab.
* i.e the new UI and its associated BrowserWindow data structure.
*/
-BrowserWindow *UIcmd_tab_new(const void *vbw)
+static BrowserWindow *UIcmd_tab_new(const void *vbw)
{
_MSG(" UIcmd_tab_new vbw=%p\n", vbw);
@@ -239,10 +493,10 @@ BrowserWindow *UIcmd_tab_new(const void *vbw)
// WORKAROUND: limit the number of tabs because of a fltk bug
if (ui->tabs()->children() >= 127)
return a_UIcmd_browser_window_new(ui->window()->w(), ui->window()->h(),
- vbw);
+ 0, vbw);
// Create and set the UI
- UI *new_ui = new UI(0, 0, ui->w(), ui->h(), "Label", ui);
+ UI *new_ui = new UI(0, 0, ui->w(), ui->h(), DEFAULT_TAB_LABEL, ui);
new_ui->tabs(ui->tabs());
new_ui->tabs()->add(new_ui);
@@ -255,7 +509,7 @@ BrowserWindow *UIcmd_tab_new(const void *vbw)
Layout *layout = new Layout (platform);
FltkViewport *viewport = new FltkViewport (0, 0, 1, 1);
-
+
layout->attachView (viewport);
new_ui->set_render_layout(*viewport);
@@ -282,10 +536,11 @@ void a_UIcmd_close_bw(void *vbw)
Layout *layout = (Layout*)bw->render_layout;
MSG("a_UIcmd_close_bw\n");
- a_Bw_stop_clients(bw, BW_Root + BW_Img + Bw_Force);
+ a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
delete(layout);
if (ui->tabs()) {
ui->tabs()->remove(ui);
+ ui->tabs()->value(ui->tabs()->children() - 1);
if (ui->tabs()->value() != -1)
ui->tabs()->selected_child()->take_focus();
else
@@ -332,7 +587,7 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
/* file URI */
ch = new_urlstr[5];
if (!ch || ch == '.') {
- url = a_Url_new(a_Dir_get_owd(), "file:");
+ url = a_Url_new(Paths::getOldWorkingDir(), "file:");
} else if (ch == '~') {
url = a_Url_new(dGethomedir(), "file:");
} else {
@@ -346,13 +601,10 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
dFree(new_urlstr);
if (url) {
- a_Nav_push(bw, url);
+ a_UIcmd_open_url(bw, url);
a_Url_free(url);
}
}
-
- /* let the rendered area have focus */
- //gtk_widget_grab_focus(GTK_BIN(bw->render_main_scroll)->child);
}
/*
@@ -360,15 +612,37 @@ void a_UIcmd_open_urlstr(void *vbw, const char *urlstr)
*/
void a_UIcmd_open_url(BrowserWindow *bw, const DilloUrl *url)
{
- a_Nav_push(bw, url);
+ a_Nav_push(bw, url, NULL);
+ if (BW2UI(bw)->get_panelmode() == UI_TEMPORARILY_SHOW_PANELS)
+ BW2UI(bw)->set_panelmode(UI_HIDDEN);
+ a_UIcmd_focus_main_area(bw);
+}
+
+static void UIcmd_open_url_nbw(BrowserWindow *new_bw, const DilloUrl *url)
+{
+ /* When opening a new BrowserWindow (tab or real window) we focus
+ * Location if we don't yet have an URL, main otherwise.
+ */
+ if (url) {
+ a_Nav_push(new_bw, url, NULL);
+ BW2UI(new_bw)->focus_main();
+ } else {
+ BW2UI(new_bw)->focus_location();
+ }
}
/*
- * Open a new URL in the given browser window
+ * Open a new URL in a new browser window
*/
void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url)
{
- a_Nav_push_nw(bw, url);
+ int w, h;
+ BrowserWindow *new_bw;
+
+ a_UIcmd_get_wh(bw, &w, &h);
+ new_bw = a_UIcmd_browser_window_new(w, h, 0, bw);
+
+ UIcmd_open_url_nbw(new_bw, url);
}
/*
@@ -377,12 +651,11 @@ void a_UIcmd_open_url_nw(BrowserWindow *bw, const DilloUrl *url)
void a_UIcmd_open_url_nt(void *vbw, const DilloUrl *url, int focus)
{
BrowserWindow *new_bw = UIcmd_tab_new(vbw);
- if (url)
- a_Nav_push(new_bw, url);
- if (focus) {
+
+ if (focus)
BW2UI(new_bw)->tabs()->selected_child(BW2UI(new_bw));
- BW2UI(new_bw)->tabs()->selected_child()->take_focus();
- }
+
+ UIcmd_open_url_nbw(new_bw, url);
}
/*
@@ -422,7 +695,7 @@ void a_UIcmd_forw_popup(void *vbw)
*/
void a_UIcmd_home(void *vbw)
{
- a_Nav_home((BrowserWindow*)vbw);
+ a_UIcmd_open_url((BrowserWindow*)vbw, prefs.home);
}
/*
@@ -434,19 +707,44 @@ void a_UIcmd_reload(void *vbw)
}
/*
+ * Repush current URL
+ */
+void a_UIcmd_repush(void *vbw)
+{
+ a_Nav_repush((BrowserWindow*)vbw);
+}
+
+/*
+ * Zero-delay URL redirection.
+ */
+void a_UIcmd_redirection0(void *vbw, const DilloUrl *url)
+{
+ a_Nav_redirection0((BrowserWindow*)vbw, url);
+}
+
+/*
* Return a suitable filename for a given URL path.
*/
static char *UIcmd_make_save_filename(const char *pathstr)
{
size_t MaxLen = 64;
- char *FileName, *name;
- const char *dir = a_UIcmd_get_save_dir();
+ char *FileName, *newname, *o, *n;
+ const char *name, *dir = a_UIcmd_get_save_dir();
if ((name = strrchr(pathstr, '/'))) {
if (strlen(++name) > MaxLen) {
name = name + strlen(name) - MaxLen;
}
- FileName = dStrconcat(dir ? dir : "", name, NULL);
+ /* Replace %20 and ' ' with '_' in Filename */
+ o = n = newname = dStrdup(name);
+ for (int i = 0; o[i]; i++) {
+ *n++ = (o[i] == ' ') ? '_' :
+ (o[i] == '%' && o[i+1] == '2' && o[i+2] == '0') ?
+ i+=2, '_' : o[i];
+ }
+ *n = 0;
+ FileName = dStrconcat(dir ? dir : "", newname, NULL);
+ dFree(newname);
} else {
FileName = dStrconcat(dir ? dir : "", pathstr, NULL);
}
@@ -466,7 +764,7 @@ const char *a_UIcmd_get_save_dir()
*/
void a_UIcmd_set_save_dir(const char *dir)
{
- char *p;
+ const char *p;
if (dir && (p = strrchr(dir, '/'))) {
dFree(save_dir);
@@ -481,24 +779,21 @@ void a_UIcmd_set_save_dir(const char *dir)
void a_UIcmd_save(void *vbw)
{
const char *name;
- char *SuggestedName, *urlstr;
- DilloUrl *url;
+ char *SuggestedName;
+ BrowserWindow *bw = (BrowserWindow *)vbw;
+ const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
- a_UIcmd_set_save_dir(prefs.save_dir);
+ if (url) {
+ a_UIcmd_set_save_dir(prefs.save_dir);
+ SuggestedName = UIcmd_make_save_filename(URL_PATH(url));
+ name = a_Dialog_save_file("Save Page as File", NULL, SuggestedName);
+ MSG("a_UIcmd_save: %s\n", name);
+ dFree(SuggestedName);
- urlstr = a_UIcmd_get_location_text((BrowserWindow*)vbw);
- url = a_Url_new(urlstr, NULL);
- SuggestedName = UIcmd_make_save_filename(URL_PATH(url));
- name = a_Dialog_save_file("Save Page as File", NULL, SuggestedName);
- MSG("a_UIcmd_save: %s\n", name);
- dFree(SuggestedName);
- dFree(urlstr);
-
- if (name) {
- a_Nav_save_url((BrowserWindow*)vbw, url, name);
+ if (name) {
+ a_Nav_save_url(bw, url, name);
+ }
}
-
- a_Url_free(url);
}
/*
@@ -519,11 +814,19 @@ void a_UIcmd_stop(void *vbw)
MSG("a_UIcmd_stop()\n");
a_Nav_cancel_expect(bw);
- a_Bw_stop_clients(bw, BW_Root + BW_Img + Bw_Force);
+ a_Bw_stop_clients(bw, BW_Root + BW_Img + BW_Force);
a_UIcmd_set_buttons_sens(bw);
}
/*
+ * Popup the tools menu
+ */
+void a_UIcmd_tools(void *vbw, void *v_wid)
+{
+ a_Menu_tools_popup((BrowserWindow*)vbw, v_wid);
+}
+
+/*
* Open URL with dialog chooser
*/
void a_UIcmd_open_file(void *vbw)
@@ -535,7 +838,7 @@ void a_UIcmd_open_file(void *vbw)
if (name) {
url = a_Url_new(name, "file:");
- a_Nav_push((BrowserWindow*)vbw, url);
+ a_UIcmd_open_url((BrowserWindow*)vbw, url);
a_Url_free(url);
dFree(name);
}
@@ -579,11 +882,12 @@ static char *UIcmd_make_search_str(const char *str)
*/
void a_UIcmd_search_dialog(void *vbw)
{
- const char *query, *url_str;
+ const char *query;
if ((query = a_Dialog_input("Search the Web:"))) {
- url_str = UIcmd_make_search_str(query);
+ char *url_str = UIcmd_make_search_str(query);
a_UIcmd_open_urlstr(vbw, url_str);
+ dFree(url_str);
}
}
@@ -625,7 +929,7 @@ void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url)
void a_UIcmd_book(void *vbw)
{
DilloUrl *url = a_Url_new("dpi:/bm/", NULL);
- a_Nav_push((BrowserWindow*)vbw, url);
+ a_UIcmd_open_url((BrowserWindow*)vbw, url);
a_Url_free(url);
}
@@ -641,10 +945,11 @@ void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url)
/*
* Popup the page menu
*/
-void a_UIcmd_page_popup(void *vbw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs)
+void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls)
{
- a_Menu_page_popup((BrowserWindow*)vbw, url, has_bugs, unloaded_imgs);
+ BrowserWindow *bw = (BrowserWindow*)vbw;
+ const DilloUrl *url = a_History_get_url(NAV_TOP_UIDX(bw));
+ a_Menu_page_popup(bw, url, has_bugs, v_cssUrls);
}
/*
@@ -659,9 +964,18 @@ void a_UIcmd_link_popup(void *vbw, const DilloUrl *url)
* Pop up the image menu
*/
void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
- DilloUrl *link_url)
+ DilloUrl *page_url, DilloUrl *link_url)
{
- a_Menu_image_popup((BrowserWindow*)vbw, url, loaded_img, link_url);
+ a_Menu_image_popup((BrowserWindow*)vbw, url, loaded_img, page_url,link_url);
+}
+
+/*
+ * Pop up the form menu
+ */
+void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
+ bool_t showing_hiddens)
+{
+ a_Menu_form_popup((BrowserWindow*)vbw, url, vform, showing_hiddens);
}
/*
@@ -682,17 +996,32 @@ void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr)
}
/*
- * Show a text window with the URL's source
+ * Ask the vsource dpi to show this URL's source
*/
-void a_UIcmd_view_page_source(const DilloUrl *url)
+void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url)
{
char *buf;
int buf_size;
+ Dstr *dstr_url;
+ DilloUrl *vs_url;
+ static int post_id = 0;
+ char tag[8];
if (a_Nav_get_buf(url, &buf, &buf_size)) {
- void *vWindow = a_Dialog_make_text_window(buf, "View Page source");
+ a_Nav_set_vsource_url(url);
+ dstr_url = dStr_new("dpi:/vsource/:");
+ dStr_append(dstr_url, URL_STR(url));
+ if (URL_FLAGS(url) & URL_Post) {
+ /* append a custom string to differentiate POST URLs */
+ post_id = (post_id < 9999) ? post_id + 1 : 0;
+ snprintf(tag, 8, "_%.4d", post_id);
+ dStr_append(dstr_url, tag);
+ }
+ vs_url = a_Url_new(dstr_url->str, NULL);
+ a_UIcmd_open_url_nt(bw, vs_url, 1);
+ a_Url_free(vs_url);
+ dStr_free(dstr_url, 1);
a_Nav_unref_buf(url);
- a_Dialog_show_text_window(vWindow);
}
}
@@ -704,9 +1033,7 @@ void a_UIcmd_view_page_bugs(void *vbw)
BrowserWindow *bw = (BrowserWindow*)vbw;
if (bw->num_page_bugs > 0) {
- void *vWindow = a_Dialog_make_text_window(bw->page_bugs->str,
- "Detected HTML errors");
- a_Dialog_show_text_window(vWindow);
+ a_Dialog_text_window(bw->page_bugs->str, "Detected HTML errors");
} else {
a_Dialog_msg("Zero detected HTML errors!");
}
@@ -805,6 +1132,40 @@ void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f)
}
/*
+ * Pass scrolling command to dw.
+ */
+void a_UIcmd_scroll(BrowserWindow *bw, int icmd)
+{
+ Layout *layout = (Layout*)bw->render_layout;
+
+ if (layout) {
+ typedef struct {
+ KeysCommand_t keys_cmd;
+ ScrollCommand dw_cmd;
+ } mapping_t;
+
+ const mapping_t map[] = {
+ {KEYS_SCREEN_UP, SCREEN_UP_CMD},
+ {KEYS_SCREEN_DOWN, SCREEN_DOWN_CMD},
+ {KEYS_LINE_UP, LINE_UP_CMD},
+ {KEYS_LINE_DOWN, LINE_DOWN_CMD},
+ {KEYS_LEFT, LEFT_CMD},
+ {KEYS_RIGHT, RIGHT_CMD},
+ {KEYS_TOP, TOP_CMD},
+ {KEYS_BOTTOM, BOTTOM_CMD},
+ };
+ KeysCommand_t keycmd = (KeysCommand_t)icmd;
+
+ for (uint_t i = 0; i < (sizeof(map)/sizeof(mapping_t)); i++) {
+ if (keycmd == map[i].keys_cmd) {
+ layout->scroll(map[i].dw_cmd);
+ break;
+ }
+ }
+ }
+}
+
+/*
* Get location's text
*/
char *a_UIcmd_get_location_text(BrowserWindow *bw)
@@ -852,12 +1213,24 @@ void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug)
}
/*
- * Set the page title.
- * now it goes to the window titlebar (maybe to TAB label in the future).
+ * Set the page title in the window titlebar and tab label.
+ * (Update window titlebar for the current tab only)
*/
void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label)
{
- BW2UI(bw)->set_page_title(label);
+ const int size = 128;
+ char title[size];
+
+ if (a_UIcmd_get_bw_by_widget(BW2UI(bw)->tabs()->selected_child()) == bw) {
+ // This is the focused bw, set window title
+ if (snprintf(title, size, "Dillo: %s", label) >= size) {
+ uint_t i = MIN(size - 4, 1 + a_Utf8_end_of_char(title, size - 8));
+ snprintf(title + i, 4, "...");
+ }
+ BW2UI(bw)->window()->copy_label(title);
+ BW2UI(bw)->window()->redraw_label();
+ }
+ BW2UI(bw)->set_tab_title(label);
}
/*
@@ -877,22 +1250,6 @@ void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...)
}
/*
- * Check whether the UI has automatic image loading enabled.
- */
-bool_t a_UIcmd_get_images_enabled(BrowserWindow *bw)
-{
- return BW2UI(bw)->images_enabled();
-}
-
-/*
- * Enable/Disable automatic image loading.
- */
-void a_UIcmd_set_images_enabled(BrowserWindow *bw, int flag)
-{
- BW2UI(bw)->images_enabled(flag);
-}
-
-/*
* Set the sensitivity of back/forw/stop buttons.
*/
void a_UIcmd_set_buttons_sens(BrowserWindow *bw)
@@ -912,6 +1269,22 @@ void a_UIcmd_set_buttons_sens(BrowserWindow *bw)
}
/*
+ * Keep track of mouse pointer over a link.
+ */
+void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag)
+{
+ BW2UI(bw)->pointerOnLink(flag);
+}
+
+/*
+ * Is the mouse pointer over a link?
+ */
+int a_UIcmd_pointer_on_link(BrowserWindow *bw)
+{
+ return BW2UI(bw)->pointerOnLink();
+}
+
+/*
* Toggle control panel (aka. fullscreen)
*/
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw)
@@ -920,23 +1293,24 @@ void a_UIcmd_fullscreen_toggle(BrowserWindow *bw)
}
/*
- * Search for next occurrence of key.
+ * Search for next/previous occurrence of key.
*/
-void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key, int case_sens)
+void a_UIcmd_findtext_search(BrowserWindow *bw, const char *key,
+ int case_sens, int backwards)
{
Layout *l = (Layout *)bw->render_layout;
-
- switch (l->search(key, case_sens)) {
- case FindtextState::RESTART:
- a_UIcmd_set_msg(bw, "No further occurrences of \"%s\". "
- "Restarting from the top.", key);
- break;
- case FindtextState::NOT_FOUND:
- a_UIcmd_set_msg(bw, "\"%s\" not found.", key);
- break;
- case FindtextState::SUCCESS:
- default:
- a_UIcmd_set_msg(bw, "");
+
+ switch (l->search(key, case_sens, backwards)) {
+ case FindtextState::RESTART:
+ a_UIcmd_set_msg(bw, "No further occurrences of \"%s\". "
+ "Restarting from the top.", key);
+ break;
+ case FindtextState::NOT_FOUND:
+ a_UIcmd_set_msg(bw, "\"%s\" not found.", key);
+ break;
+ case FindtextState::SUCCESS:
+ default:
+ a_UIcmd_set_msg(bw, "");
}
}
diff --git a/src/uicmd.hh b/src/uicmd.hh
index 02dbae8a..c8fea9e7 100644
--- a/src/uicmd.hh
+++ b/src/uicmd.hh
@@ -8,7 +8,8 @@ extern "C" {
#endif /* __cplusplus */
-BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh, const void *v_bw);
+BrowserWindow *a_UIcmd_browser_window_new(int ww, int wh,
+ uint32_t xid, const void *v_bw);
BrowserWindow *a_UIcmd_get_bw_by_widget(void *v_wid);
void a_UIcmd_send_event_to_tabs_by_wid(int e, void *v_wid);
void a_UIcmd_open_urlstr(void *vbw, const char *urlstr);
@@ -21,8 +22,11 @@ void a_UIcmd_forw(void *vbw);
void a_UIcmd_forw_popup(void *vbw);
void a_UIcmd_home(void *vbw);
void a_UIcmd_reload(void *vbw);
+void a_UIcmd_repush(void *vbw);
+void a_UIcmd_redirection0(void *vbw, const DilloUrl *url);
void a_UIcmd_save(void *vbw);
void a_UIcmd_stop(void *vbw);
+void a_UIcmd_tools(void *vbw, void *v_wid);
void a_UIcmd_save_link(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_open_file(void *vbw);
const char *a_UIcmd_select_file();
@@ -32,18 +36,20 @@ void a_UIcmd_book(void *vbw);
void a_UIcmd_add_bookmark(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw);
void a_UIcmd_findtext_dialog(BrowserWindow *bw);
-void a_UIcmd_findtext_search(BrowserWindow *bw,const char *key,int case_sens);
+void a_UIcmd_findtext_search(BrowserWindow *bw,const char *key,int case_sens,
+ int backwards);
void a_UIcmd_findtext_reset(BrowserWindow *bw);
void a_UIcmd_focus_main_area(BrowserWindow *bw);
void a_UIcmd_focus_location(void *vbw);
-void a_UIcmd_page_popup(void *vbw, const DilloUrl *url,
- bool_t has_bugs, bool_t unloaded_imgs);
+void a_UIcmd_page_popup(void *vbw, bool_t has_bugs, void *v_cssUrls);
void a_UIcmd_link_popup(void *vbw, const DilloUrl *url);
void a_UIcmd_image_popup(void *vbw, const DilloUrl *url, bool_t loaded_img,
- DilloUrl *link_url);
+ DilloUrl *page_url, DilloUrl *link_url);
+void a_UIcmd_form_popup(void *vbw, const DilloUrl *url, void *vform,
+ bool_t showing_hiddens);
void a_UIcmd_file_popup(void *vbw, void *v_wid);
void a_UIcmd_copy_urlstr(BrowserWindow *bw, const char *urlstr);
-void a_UIcmd_view_page_source(const DilloUrl *url);
+void a_UIcmd_view_page_source(BrowserWindow *bw, const DilloUrl *url);
void a_UIcmd_view_page_bugs(void *vbw);
void a_UIcmd_bugmeter_popup(void *vbw);
int *a_UIcmd_get_history(BrowserWindow *bw, int direction);
@@ -62,6 +68,7 @@ void a_UIcmd_get_wh(BrowserWindow *bw, int *w, int *h);
void a_UIcmd_get_scroll_xy(BrowserWindow *bw, int *x, int *y);
void a_UIcmd_set_scroll_xy(BrowserWindow *bw, int x, int y);
void a_UIcmd_set_scroll_by_fragment(BrowserWindow *bw, const char *f);
+void a_UIcmd_scroll(BrowserWindow *bw, int icmd);
char *a_UIcmd_get_location_text(BrowserWindow *bw);
void a_UIcmd_set_location_text(void *vbw, const char *text);
void a_UIcmd_set_page_prog(BrowserWindow *bw, size_t nbytes, int cmd);
@@ -69,11 +76,10 @@ void a_UIcmd_set_img_prog(BrowserWindow *bw, int n_img, int t_img, int cmd);
void a_UIcmd_set_bug_prog(BrowserWindow *bw, int n_bug);
void a_UIcmd_set_page_title(BrowserWindow *bw, const char *label);
void a_UIcmd_set_msg(BrowserWindow *bw, const char *format, ...);
-bool_t a_UIcmd_get_images_enabled(BrowserWindow *bw);
-void a_UIcmd_set_images_enabled(BrowserWindow *bw, int flag);
void a_UIcmd_set_buttons_sens(BrowserWindow *bw);
void a_UIcmd_fullscreen_toggle(BrowserWindow *bw);
-
+void a_UIcmd_set_pointer_on_link(BrowserWindow *bw, int flag);
+int a_UIcmd_pointer_on_link(BrowserWindow *bw);
#ifdef __cplusplus
}
diff --git a/src/url.c b/src/url.c
index 3fb9122a..acc8aa72 100644
--- a/src/url.c
+++ b/src/url.c
@@ -2,7 +2,7 @@
* File: url.c
*
* Copyright (C) 2001 Livio Baldini Soares <livio@linux.ime.usp.br>
- * Copyright (C) 2001-2007 Jorge Arellano Cid <jcid@dillo.org>
+ * Copyright (C) 2001-2009 Jorge Arellano Cid <jcid@dillo.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
*/
/*
- * Regular Expression as given in RFC2396 for URL parsing.
+ * Regular Expression as given in RFC3986 for URL parsing.
*
* ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
* 12 3 4 5 6 7 8 9
@@ -42,8 +42,6 @@
* - path is never "undefined" though it may be "empty".
*/
-
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
@@ -114,7 +112,7 @@ const char *a_Url_hostname(const DilloUrl *u)
if ((p = strchr(url->authority, ':'))) {
url->port = strtol(p + 1, NULL, 10);
url->hostname = dStrndup(url->authority,
- (uint_t)(p - url->authority));
+ (uint_t)(p - url->authority));
} else {
url->hostname = url->authority;
}
@@ -190,6 +188,7 @@ static DilloUrl *Url_object_new(const char *uri_str)
/*
* Free a DilloUrl
+ * Do nothing if the argument is NULL
*/
void a_Url_free(DilloUrl *url)
{
@@ -206,7 +205,7 @@ void a_Url_free(DilloUrl *url)
}
/*
- * Resolve the URL as RFC2396 suggests.
+ * Resolve the URL as RFC3986 suggests.
*/
static Dstr *Url_resolve_relative(const char *RelStr,
DilloUrl *BaseUrlPar,
@@ -230,14 +229,19 @@ static Dstr *Url_resolve_relative(const char *RelStr,
SolvedUrl = dStr_sized_new(64);
Path = dStr_sized_new(64);
- /* path empty && scheme, authority and query undefined */
- if (!RelUrl->path && !RelUrl->scheme &&
- !RelUrl->authority && !RelUrl->query) {
+ /* path empty && scheme and authority undefined */
+ if (!RelUrl->path && !RelUrl->scheme && !RelUrl->authority) {
dStr_append(SolvedUrl, BaseStr);
-
+ if ((p = strchr(SolvedUrl->str, '#')))
+ dStr_truncate(SolvedUrl, p - SolvedUrl->str);
+
+ if (RelUrl->query) { /* query */
+ if (BaseUrl->query)
+ dStr_truncate(SolvedUrl, BaseUrl->query - BaseUrl->buffer - 1);
+ dStr_append_c(SolvedUrl, '?');
+ dStr_append(SolvedUrl, RelUrl->query);
+ }
if (RelUrl->fragment) { /* fragment */
- if (BaseUrl->fragment)
- dStr_truncate(SolvedUrl, BaseUrl->fragment-BaseUrl->buffer-1);
dStr_append_c(SolvedUrl, '#');
dStr_append(SolvedUrl, RelUrl->fragment);
}
@@ -252,14 +256,12 @@ static Dstr *Url_resolve_relative(const char *RelStr,
if (RelUrl->path)
dStr_append(Path, RelUrl->path);
- } else if (RelUrl->path && RelUrl->path[0] == '/') { /* path */
- dStr_append(Path, RelUrl->path);
-
} else {
- // solve relative path
- if (BaseUrl->path) {
+ if (RelUrl->path && RelUrl->path[0] == '/') { /* absolute path */
+ ; /* Ignore BaseUrl path */
+ } else if (BaseUrl->path) { /* relative path */
dStr_append(Path, BaseUrl->path);
- for (i = Path->len; --i >= 0 && Path->str[i] != '/'; );
+ for (i = Path->len; --i >= 0 && Path->str[i] != '/'; ) ;
if (Path->str[i] == '/')
dStr_truncate(Path, ++i);
}
@@ -278,14 +280,10 @@ static Dstr *Url_resolve_relative(const char *RelStr,
// erase "<segment>/../" and "<segment>/.."
s = p = Path->str;
while ( (p = strstr(p, "/..")) != NULL ) {
- if ((p[3] == '/' || !p[3]) && (p - s)) { // "/../" | "/.."
-
- for (e = p + 3 ; p[-1] != '/' && p > s; --p);
- if (p[0] != '.' || p[1] != '.' || p[2] != '/') {
- dStr_erase(Path, p - Path->str, e - p + (*e != 0));
- p -= (p > Path->str);
- } else
- p = e;
+ if (p[3] == '/' || !p[3]) { // "/../" | "/.."
+ for (e = p + 3 ; p > s && p[-1] != '/'; --p) ;
+ dStr_erase(Path, p - Path->str, e - p + (p > s && *e != 0));
+ p -= (p > Path->str);
} else
p += 3;
}
@@ -578,7 +576,7 @@ char *a_Url_decode_hex_str(const char *str)
*/
char *a_Url_encode_hex_str(const char *str)
{
- static const char *verbatim = "-_.*";
+ static const char *const verbatim = "-_.*";
char *newstr, *c;
if (!str)
@@ -587,7 +585,7 @@ char *a_Url_encode_hex_str(const char *str)
newstr = dNew(char, 6*strlen(str)+1);
for (c = newstr; *str; str++)
- if ((isalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
+ if ((dIsalnum(*str) && !(*str & 0x80)) || strchr(verbatim, *str))
/* we really need isalnum for the "C" locale */
*c++ = *str;
else if (*str == ' ')
@@ -611,7 +609,7 @@ char *a_Url_encode_hex_str(const char *str)
/*
- * RFC-2396 suggests this stripping when "importing" URLs from other media.
+ * RFC-3986 suggests this stripping when "importing" URLs from other media.
* Strip: "URL:", enclosing < >, and embedded whitespace.
* (We also strip illegal chars: 00-1F and 7F)
*/
@@ -636,3 +634,121 @@ char *a_Url_string_strip_delimiters(const char *str)
}
return new_str;
}
+
+/*
+ * Is the provided hostname an IP address?
+ */
+static bool_t Url_host_is_ip(const char *host)
+{
+ uint_t len;
+
+ if (!host || !*host)
+ return FALSE;
+
+ len = strlen(host);
+
+ if (len == strspn(host, "0123456789.")) {
+ _MSG("an IPv4 address\n");
+ return TRUE;
+ }
+ if (*host == '[' &&
+ (len == strspn(host, "0123456789abcdefABCDEF:.[]"))) {
+ /* The precise format is shown in section 3.2.2 of rfc 3986 */
+ _MSG("an IPv6 address\n");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * How many internal dots are in the public portion of this hostname?
+ * e.g., for "www.dillo.org", it is one because everything under "dillo.org",
+ * as a .org domain, is part of one organization.
+ *
+ * Of course this is only a simple and imperfect approximation of
+ * organizational boundaries.
+ */
+static uint_t Url_host_public_internal_dots(const char *host)
+{
+ uint_t ret = 1;
+
+ if (host) {
+ int start, after, tld_len;
+
+ /* We may be able to trust the format of the host string more than
+ * I am here. Trailing dots and no dots are real possibilities, though.
+ */
+ after = strlen(host);
+ if (after > 0 && host[after - 1] == '.')
+ after--;
+ start = after;
+ while (start > 0 && host[start - 1] != '.')
+ start--;
+ tld_len = after - start;
+
+ if (tld_len > 0) {
+ /* These TLDs were chosen by examining the current publicsuffix list
+ * in January 2010 and picking out those where it was simplest for
+ * them to describe the situation by beginning with a "*.[tld]" rule.
+ */
+ const char *const tlds[] = {"ar","au","bd","bn","bt","ck","cy","do",
+ "eg","er","et","fj","fk","gt","gu","id",
+ "il","jm","ke","kh","kw","ml","mm","mt",
+ "mz","ni","np","nz","om","pg","py","qa",
+ "sv","tr","uk","uy","ve","ye","yu","za",
+ "zm","zw"};
+ uint_t i, tld_num = sizeof(tlds) / sizeof(tlds[0]);
+
+ for (i = 0; i < tld_num; i++) {
+ if (strlen(tlds[i]) == (uint_t) tld_len &&
+ !dStrncasecmp(tlds[i], host + start, tld_len)) {
+ _MSG("TLD code matched %s\n", tlds[i]);
+ ret++;
+ break;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Given a URL host string, return the portion that is public, i.e., the
+ * domain that is in a registry outside the organization.
+ * For 'www.dillo.org', that would be 'dillo.org'.
+ */
+const char *a_Url_host_find_public_suffix(const char *host)
+{
+ const char *s;
+ uint_t dots;
+
+ if (!host || !*host || Url_host_is_ip(host))
+ return host;
+
+ s = host;
+
+ while (s[1])
+ s++;
+
+ if (s > host && *s == '.') {
+ /* don't want to deal with trailing dot */
+ s--;
+ }
+
+ dots = Url_host_public_internal_dots(host);
+
+ /* With a proper host string, we should not be pointing to a dot now. */
+
+ while (s > host) {
+ if (s[-1] == '.') {
+ if (dots == 0)
+ break;
+ else
+ dots--;
+ }
+ s--;
+ }
+
+ _MSG("public suffix of %s is %s\n", host, s);
+ return s;
+}
diff --git a/src/url.h b/src/url.h
index 32b173e6..503eec60 100644
--- a/src/url.h
+++ b/src/url.h
@@ -10,7 +10,6 @@
#ifndef __URL_H__
#define __URL_H__
-#include <string.h> /* for strcmp */
#include "d_size.h"
#include "../dlib/dlib.h"
@@ -39,7 +38,7 @@
#define URL_ReloadPage (1 << 7)
#define URL_ReloadFromCache (1 << 8)
-#define URL_ReloadIncomplete (1 << 9)
+#define URL_IgnoreScroll (1 << 9)
#define URL_SpamSafe (1 << 10)
#define URL_MultipartEnc (1 << 11)
@@ -95,12 +94,12 @@ extern "C" {
struct _DilloUrl {
Dstr *url_string;
const char *buffer;
- const char *scheme; //
- const char *authority; //
- const char *path; // These are references only
- const char *query; // (no need to free them)
- const char *fragment; //
- const char *hostname; //
+ const char *scheme; /**/
+ const char *authority; /**/
+ const char *path; /* These are references only */
+ const char *query; /* (no need to free them) */
+ const char *fragment; /**/
+ const char *hostname; /**/
int port;
int flags;
Dstr *data; /* POST */
@@ -124,7 +123,7 @@ void a_Url_set_ismap_coords(DilloUrl *u, char *coord_str);
char *a_Url_decode_hex_str(const char *str);
char *a_Url_encode_hex_str(const char *str);
char *a_Url_string_strip_delimiters(const char *str);
-
+const char *a_Url_host_find_public_suffix(const char *host);
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/src/utf8.cc b/src/utf8.cc
new file mode 100644
index 00000000..0138c616
--- /dev/null
+++ b/src/utf8.cc
@@ -0,0 +1,102 @@
+/*
+ * File: utf8.c
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <fltk/utf.h>
+
+#include "../dlib/dlib.h" /* TRUE/FALSE */
+#include "utf8.hh"
+
+// C++ functions with C linkage ----------------------------------------------
+
+/*
+ * Return index of the last byte of the UTF-8-encoded character that str + i
+ * points to or into.
+ */
+uint_t a_Utf8_end_of_char(const char *str, uint_t i)
+{
+ /* We can almost get what we want from utf8fwd(p+1,...)-1, but that
+ * does not work for the last character in a string, and the fn makes some
+ * assumptions that do not suit us.
+ * Here's something very simpleminded instead:
+ */
+ if (str && *str && (str[i] & 0x80)) {
+ int internal_bytes = (str[i] & 0x40) ? 0 : 1;
+
+ while (((str[i + 1] & 0xc0) == 0x80) && (++internal_bytes < 4))
+ i++;
+ }
+ return i;
+}
+
+/*
+ * Decode a single UTF-8-encoded character starting at p.
+ * The resulting Unicode value (in the range 0-0x10ffff) is returned,
+ * and len is set to the number of bytes in the UTF-8 encoding.
+ * Note that utf8decode(), if given non-UTF-8 data, will interpret
+ * it as ISO-8859-1 or CP1252 if possible.
+ */
+uint_t a_Utf8_decode(const char* str, const char* end, int* len)
+{
+ return utf8decode(str, end, len);
+}
+
+/*
+ * Write UTF-8 encoding of ucs into buf and return number of bytes written.
+ */
+int a_Utf8_encode(unsigned int ucs, char *buf)
+{
+ return utf8encode(ucs, buf);
+}
+
+/*
+ * Examine first srclen bytes of src.
+ * Return 0 if not legal UTF-8, 1 if all ASCII, 2 if all below 0x800,
+ * 3 if all below 0x10000, and 4 otherwise.
+ */
+int a_Utf8_test(const char* src, unsigned int srclen)
+{
+ return utf8test(src, srclen);
+}
+
+/*
+ * Does s point to a UTF-8-encoded ideographic character?
+ *
+ * This is based on http://unicode.org/reports/tr14/#ID plus some guesses
+ * for what might make the most sense for Dillo. Surprisingly, they include
+ * Hangul Compatibility Jamo, but they're the experts, so I'll follow along.
+ */
+bool_t a_Utf8_ideographic(const char *s, const char *end, int *len)
+{
+ bool_t ret = FALSE;
+
+ if ((uchar_t)*s >= 0xe2) {
+ /* Unicode char >= U+2000. */
+ unsigned unicode = a_Utf8_decode(s, end, len);
+
+ if (unicode >= 0x2e80 &&
+ ((unicode <= 0xa4cf) ||
+ (unicode >= 0xf900 && unicode <= 0xfaff) ||
+ (unicode >= 0xff00 && unicode <= 0xff9f))) {
+ ret = TRUE;
+ }
+ } else {
+ *len = 1 + (int)a_Utf8_end_of_char(s, 0);
+ }
+ return ret;
+}
+
+bool_t a_Utf8_combining_char(int unicode)
+{
+ return ((unicode >= 0x0300 && unicode <= 0x036f) ||
+ (unicode >= 0x1dc0 && unicode <= 0x1dff) ||
+ (unicode >= 0x20d0 && unicode <= 0x20ff) ||
+ (unicode >= 0xfe20 && unicode <= 0xfe2f));
+}
diff --git a/src/utf8.hh b/src/utf8.hh
new file mode 100644
index 00000000..4ded50b8
--- /dev/null
+++ b/src/utf8.hh
@@ -0,0 +1,33 @@
+#ifndef __UTF8_HH__
+#define __UTF8_HH__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#include "d_size.h"
+
+/*
+ * Unicode replacement character U+FFFD
+ * "used to replace an incoming character whose value is unknown or otherwise
+ * unrepresentable in Unicode"
+ */
+static const char utf8_replacement_char[] = "\xEF\xBF\xBD";
+
+/* Unicode zero width space U+200B */
+static const char utf8_zero_width_space[] = "\xE2\x80\x8B";
+
+uint_t a_Utf8_end_of_char(const char *str, uint_t i);
+uint_t a_Utf8_decode(const char*, const char* end, int* len);
+int a_Utf8_encode(unsigned int ucs, char *buf);
+int a_Utf8_test(const char* src, unsigned int srclen);
+bool_t a_Utf8_ideographic(const char *s, const char *end, int *len);
+bool_t a_Utf8_combining_char(int unicode);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __UTF8_HH__ */
+
diff --git a/src/web.cc b/src/web.cc
index b9141913..17302b1c 100644
--- a/src/web.cc
+++ b/src/web.cc
@@ -9,10 +9,6 @@
* (at your option) any later version.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h> /* for rint */
-
#include "msg.h"
#include "nav.h"
@@ -22,7 +18,7 @@
#include "IO/mime.h"
#include "dw/core.hh"
-#include "prefs.h"
+#include "styleengine.hh"
#include "web.hh"
// Platform independent part
@@ -54,9 +50,6 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
CA_Callback_t *Call, void **Data)
{
Widget *dw = NULL;
- style::StyleAttrs styleAttrs;
- style::Style *widgetStyle;
- style::FontAttrs fontAttrs;
_MSG("a_Web_dispatch_by_type\n");
@@ -67,25 +60,17 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
if (Web->flags & WEB_RootUrl) {
/* We have RootUrl! */
+
+ /* Set a style for the widget */
+ StyleEngine styleEngine (layout);
+ styleEngine.startElement ("body");
+ Web->bgColor= styleEngine.backgroundStyle()->backgroundColor->getColor();
+
dw = (Widget*) a_Mime_set_viewer(Type, Web, Call, Data);
if (dw == NULL)
return -1;
- /* Set a style for the widget */
- fontAttrs.name = prefs.vw_fontname;
- fontAttrs.size = (int) rint(14.0 * prefs.font_factor);
- fontAttrs.weight = 400;
- fontAttrs.style = style::FONT_STYLE_NORMAL;
-
- styleAttrs.initValues ();
- styleAttrs.margin.setVal (5);
- styleAttrs.font = style::Font::create (layout, &fontAttrs);
- styleAttrs.color = style::Color::createSimple (layout, 0xff0000);
- styleAttrs.backgroundColor =
- style::Color::createSimple (layout, prefs.bg_color);
- widgetStyle = style::Style::create (layout, &styleAttrs);
- dw->setStyle (widgetStyle);
- widgetStyle->unref ();
+ dw->setStyle (styleEngine.style ());
/* This method frees the old dw if any */
layout->setWidget(dw);
@@ -118,19 +103,21 @@ int a_Web_dispatch_by_type (const char *Type, DilloWeb *Web,
/*
* Allocate and set safe values for a DilloWeb structure
*/
-DilloWeb* a_Web_new(const DilloUrl *url)
+DilloWeb* a_Web_new(const DilloUrl *url, const DilloUrl *requester)
{
DilloWeb *web= dNew(DilloWeb, 1);
_MSG(" a_Web_new: ValidWebs ==> %d\n", dList_length(ValidWebs));
web->url = a_Url_dup(url);
+ web->requester = a_Url_dup(requester);
web->bw = NULL;
web->flags = 0;
web->Image = NULL;
web->filename = NULL;
web->stream = NULL;
web->SavedBytes = 0;
-
+ web->bgColor = 0x000000; /* Dummy value will be overwritten
+ * in a_Web_dispatch_by_type. */
dList_append(ValidWebs, (void *)web);
return web;
}
@@ -149,12 +136,12 @@ int a_Web_valid(DilloWeb *web)
void a_Web_free(DilloWeb *web)
{
if (!web) return;
- if (web->url)
- a_Url_free(web->url);
- if (web->Image)
- a_Image_unref(web->Image);
+ a_Url_free(web->url);
+ a_Url_free(web->requester);
+ a_Image_unref(web->Image);
dFree(web->filename);
dList_remove(ValidWebs, (void *)web);
+ _MSG("a_Web_free: ValidWebs=%d\n", dList_length(ValidWebs));
dFree(web);
}
diff --git a/src/web.hh b/src/web.hh
index a5e05a2f..d017903a 100644
--- a/src/web.hh
+++ b/src/web.hh
@@ -22,18 +22,21 @@ typedef struct _DilloWeb DilloWeb;
struct _DilloWeb {
DilloUrl *url; /* Requested URL */
+ DilloUrl *requester; /* URL that caused this request, or
+ * NULL if user-initiated. */
BrowserWindow *bw; /* The requesting browser window [reference] */
int flags; /* Additional info */
DilloImage *Image; /* For image urls [reference] */
+ int32_t bgColor; /* for image backgrounds */
char *filename; /* Variables for Local saving */
FILE *stream;
int SavedBytes;
};
void a_Web_init(void);
-DilloWeb* a_Web_new (const DilloUrl* url);
+DilloWeb* a_Web_new (const DilloUrl* url, const DilloUrl *requester);
int a_Web_valid(DilloWeb *web);
void a_Web_free (DilloWeb*);
int a_Web_dispatch_by_type (const char *Type, DilloWeb *web,
diff --git a/src/xembed.cc b/src/xembed.cc
new file mode 100644
index 00000000..04a4362a
--- /dev/null
+++ b/src/xembed.cc
@@ -0,0 +1,165 @@
+/*
+ * File: xembed.cc
+ *
+ * Copyright (C) 2009 Jorge Arellano Cid <jcid@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <string.h>
+#include <ctype.h>
+
+#include <fltk/Window.h>
+#include <fltk/run.h>
+#include <fltk/events.h>
+#include <fltk/x.h>
+
+#include "xembed.hh"
+
+#if USE_X11
+
+typedef enum {
+ XEMBED_EMBEDDED_NOTIFY = 0,
+ XEMBED_WINDOW_ACTIVATE = 1,
+ XEMBED_WINDOW_DEACTIVATE = 2,
+ XEMBED_REQUEST_FOCUS = 3,
+ XEMBED_FOCUS_IN = 4,
+ XEMBED_FOCUS_OUT = 5,
+ XEMBED_FOCUS_NEXT = 6,
+ XEMBED_FOCUS_PREV = 7,
+ XEMBED_GRAB_KEY = 8,
+ XEMBED_UNGRAB_KEY = 9,
+ XEMBED_MODALITY_ON = 10,
+ XEMBED_MODALITY_OFF = 11,
+} XEmbedMessageType;
+
+void
+Xembed::setXembedInfo(unsigned long flags)
+{
+ unsigned long buffer[2];
+
+ Atom xembed_info_atom = XInternAtom (fltk::xdisplay, "_XEMBED_INFO", false);
+
+ buffer[0] = 1;
+ buffer[1] = flags;
+
+ XChangeProperty (fltk::xdisplay,
+ xid,
+ xembed_info_atom, xembed_info_atom, 32,
+ PropModeReplace,
+ (unsigned char *)buffer, 2);
+}
+
+void
+Xembed::sendXembedEvent(uint32_t message) {
+ XClientMessageEvent xclient;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.window = xid;
+ xclient.type = ClientMessage;
+ xclient.message_type = XInternAtom (fltk::xdisplay, "_XEMBED", false);
+ xclient.format = 32;
+ xclient.data.l[0] = fltk::event_time;
+ xclient.data.l[1] = message;
+
+ XSendEvent(fltk::xdisplay, xid, False, NoEventMask, (XEvent *)&xclient);
+ XSync(fltk::xdisplay, False);
+}
+
+int
+Xembed::handle(int e) {
+ if (e == fltk::PUSH)
+ sendXembedEvent(XEMBED_REQUEST_FOCUS);
+
+ return Window::handle(e);
+}
+
+static int event_handler(int e, fltk::Window *w) {
+ Atom xembed_atom = XInternAtom (fltk::xdisplay, "_XEMBED", false);
+
+ if (fltk::xevent.type == ClientMessage) {
+ if (fltk::xevent.xclient.message_type == xembed_atom) {
+ long message = fltk::xevent.xclient.data.l[1];
+
+ switch (message) {
+ case XEMBED_WINDOW_ACTIVATE:
+ // Force a ConfigureNotify message so fltk can get the new
+ // coordinates after a move of the embedder window.
+ w->resize(0, 0, w->w(), w->h());
+ break;
+ case XEMBED_WINDOW_DEACTIVATE:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// TODO: Implement more XEMBED support;
+
+void Xembed::create() {
+ createInternal(xid);
+ setXembedInfo(1);
+ fltk::add_event_handler(event_handler);
+}
+
+void Xembed::createInternal(uint32_t parent) {
+ fltk::Window *window = this;
+ Colormap colormap = fltk::xcolormap;
+
+ XSetWindowAttributes attr;
+ attr.border_pixel = 0;
+ attr.colormap = colormap;
+ attr.bit_gravity = 0; // StaticGravity;
+ int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
+
+ int W = window->w();
+ if (W <= 0) W = 1; // X don't like zero...
+ int H = window->h();
+ if (H <= 0) H = 1; // X don't like zero...
+ int X = window->x();
+ int Y = window->y();
+
+ attr.event_mask =
+ ExposureMask | StructureNotifyMask
+ | KeyPressMask | KeyReleaseMask | KeymapStateMask | FocusChangeMask
+ | ButtonPressMask | ButtonReleaseMask
+ | EnterWindowMask | LeaveWindowMask
+ | PointerMotionMask;
+
+ fltk::CreatedWindow::set_xid(window,
+ XCreateWindow(fltk::xdisplay,
+ parent,
+ X, Y, W, H,
+ 0, // borderwidth
+ fltk::xvisual->depth,
+ InputOutput,
+ fltk::xvisual->visual,
+ mask, &attr));
+}
+
+#else // USE_X11
+
+void
+Xembed::setXembedInfo(unsigned long flags) {};
+
+void
+Xembed::sendXembedEvent(uint32_t message) {};
+
+int
+Xembed::handle(int e) {
+ return Window::handle(e);
+}
+
+void
+Xembed::create() {
+ Window::create();
+}
+
+#endif
diff --git a/src/xembed.hh b/src/xembed.hh
new file mode 100644
index 00000000..70d79c5f
--- /dev/null
+++ b/src/xembed.hh
@@ -0,0 +1,23 @@
+#ifndef __XEMBED_HH__
+#define __XEMBED_HH__
+
+#include <fltk/Window.h>
+
+#include "d_size.h"
+
+class Xembed : public fltk::Window {
+ private:
+ uint32_t xid;
+ void createInternal(uint32_t parent);
+ void setXembedInfo(unsigned long flags);
+ void sendXembedEvent(uint32_t message);
+
+ public:
+ Xembed(uint32_t xid, int _w, int _h) : fltk::Window(_w, _h) {
+ this->xid = xid;
+ };
+ void create();
+ int handle(int event);
+};
+
+#endif
diff --git a/test/Makefile.am b/test/Makefile.am
index e6946adf..709196f4 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -19,7 +19,8 @@ noinst_PROGRAMS = \
dw-resource-test \
dw-ui-test \
fltk-browser \
- shapes
+ shapes \
+ cookies
dw_anchors_test_SOURCES = dw_anchors_test.cc
dw_anchors_test_LDADD = \
@@ -160,3 +161,8 @@ shapes_SOURCES = shapes.cc
shapes_LDADD = \
../dw/libDw-core.a \
../lout/liblout.a
+
+cookies_SOURCES = cookies.c
+cookies_LDADD = \
+ ../dpip/libDpip.a \
+ ../dlib/libDlib.a
diff --git a/test/cookies.c b/test/cookies.c
new file mode 100644
index 00000000..af59cb48
--- /dev/null
+++ b/test/cookies.c
@@ -0,0 +1,970 @@
+/*
+ * Dillo cookies test
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This has a big blob of the current src/IO/dpi.c in it.
+ * I hope there's a better way.
+ */
+
+#include <stdlib.h> /* malloc, etc. */
+#include <unistd.h> /* read, etc. */
+#include <stdio.h>
+#include <stdarg.h> /* va_list */
+#include <string.h> /* strchr */
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+/* net */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+
+#define _MSG(...)
+
+#define MSG_INNARDS(prefix, ...) \
+ D_STMT_START { \
+ printf(prefix __VA_ARGS__); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define MSG(...) MSG_INNARDS("", __VA_ARGS__)
+#define MSG_ERR(...) MSG_INNARDS("** ERROR **: ", __VA_ARGS__)
+
+
+#include "../dlib/dlib.h"
+#include "../dpip/dpip.h"
+
+static uint_t failed = 0;
+static uint_t passed = 0;
+
+static char SharedKey[32];
+
+/*
+ * Read all the available data from a filedescriptor.
+ * This is intended for short answers, i.e. when we know the server
+ * will write it all before being preempted. For answers that may come
+ * as an stream with delays, non-blocking is better.
+ * Return value: read data, or NULL on error and no data.
+ */
+static char *Dpi_blocking_read(int fd)
+{
+ int st;
+ const int buf_sz = 8*1024;
+ char buf[buf_sz], *msg = NULL;
+ Dstr *dstr = dStr_sized_new(buf_sz);
+
+ do {
+ st = read(fd, buf, buf_sz);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_read] %s\n", dStrerror(errno));
+ break;
+ }
+ } else if (st > 0) {
+ dStr_append_l(dstr, buf, st);
+ }
+ } while (st == buf_sz);
+
+ msg = (dstr->len > 0) ? dstr->str : NULL;
+ dStr_free(dstr, (dstr->len > 0) ? FALSE : TRUE);
+ return msg;
+}
+
+static void Dpi_close_fd(int fd)
+{
+ int st;
+
+ dReturn_if (fd < 0);
+ do
+ st = close(fd);
+ while (st < 0 && errno == EINTR);
+}
+
+static int Dpi_make_socket_fd()
+{
+ int fd, ret = -1;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
+ ret = fd;
+ }
+ return ret;
+}
+
+/*
+ * Read dpid's communication keys from its saved file.
+ * Return value: 1 on success, -1 on error.
+ */
+static int Dpi_read_comm_keys(int *port)
+{
+ FILE *In;
+ char *fname, *rcline = NULL, *tail;
+ int i, ret = -1;
+
+ fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL);
+ if ((In = fopen(fname, "r")) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] %s\n", dStrerror(errno));
+ } else if ((rcline = dGetline(In)) == NULL) {
+ MSG_ERR("[Dpi_read_comm_keys] empty file: %s\n", fname);
+ } else {
+ *port = strtol(rcline, &tail, 10);
+ for (i = 0; *tail && isxdigit(tail[i+1]); ++i)
+ SharedKey[i] = tail[i+1];
+ SharedKey[i] = 0;
+ ret = 1;
+ }
+ if (In)
+ fclose(In);
+ dFree(rcline);
+ dFree(fname);
+
+ return ret;
+}
+
+static int Dpi_check_dpid_ids()
+{
+ struct sockaddr_in sin;
+ const socklen_t sin_sz = sizeof(sin);
+ int sock_fd, dpid_port, ret = -1;
+
+ /* socket connection test */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
+ MSG("Dpi_check_dpid_ids: sock_fd=%d %s\n", sock_fd, dStrerror(errno));
+ } else if (connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_check_dpid_ids: %s\n", dStrerror(errno));
+ } else {
+ Dpi_close_fd(sock_fd);
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
+static int Dpi_blocking_write(int fd, const char *msg, int msg_len)
+{
+ int st, sent = 0;
+
+ while (sent < msg_len) {
+ st = write(fd, msg + sent, msg_len - sent);
+ if (st < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ MSG_ERR("[Dpi_blocking_write] %s\n", dStrerror(errno));
+ break;
+ }
+ }
+ sent += st;
+ }
+
+ return (sent == msg_len) ? 1 : -1;
+}
+
+/*
+ * Start dpid.
+ * Return: 0 starting now, 1 Error.
+ */
+static int Dpi_start_dpid(void)
+{
+ pid_t pid;
+ int st_pipe[2], ret = 1;
+ char *answer;
+
+ /* create a pipe to track our child's status */
+ if (pipe(st_pipe))
+ return 1;
+
+ pid = fork();
+ if (pid == 0) {
+ /* This is the child process. Execute the command. */
+ char *path1 = dStrconcat(dGethomedir(), "/.dillo/dpid", NULL);
+ Dpi_close_fd(st_pipe[0]);
+ if (execl(path1, "dpid", (char*)NULL) == -1) {
+ dFree(path1);
+ if (execlp("dpid", "dpid", (char*)NULL) == -1) {
+ MSG("Dpi_start_dpid (child): %s\n", dStrerror(errno));
+ if (Dpi_blocking_write(st_pipe[1], "ERROR", 5) == -1) {
+ MSG("Dpi_start_dpid (child): can't write to pipe.\n");
+ }
+ Dpi_close_fd(st_pipe[1]);
+ _exit (EXIT_FAILURE);
+ }
+ }
+ } else if (pid < 0) {
+ /* The fork failed. Report failure. */
+ MSG("Dpi_start_dpid: %s\n", dStrerror(errno));
+ /* close the unused pipe */
+ Dpi_close_fd(st_pipe[0]);
+ Dpi_close_fd(st_pipe[1]);
+
+ } else {
+ /* This is the parent process, check our child status... */
+ Dpi_close_fd(st_pipe[1]);
+ if ((answer = Dpi_blocking_read(st_pipe[0])) != NULL) {
+ MSG("Dpi_start_dpid: can't start dpid\n");
+ dFree(answer);
+ } else {
+ ret = 0;
+ }
+ Dpi_close_fd(st_pipe[0]);
+ }
+
+ return ret;
+}
+
+/*
+ * Confirm that the dpid is running. If not, start it.
+ * Return: 0 running OK, 1 starting (EAGAIN), 2 Error.
+ */
+static int Dpi_check_dpid(int num_tries)
+{
+ static int starting = 0;
+ int check_st = 1, ret = 2;
+
+ check_st = Dpi_check_dpid_ids();
+ _MSG("Dpi_check_dpid: check_st=%d\n", check_st);
+
+ if (check_st == 1) {
+ /* connection test with dpi server passed */
+ starting = 0;
+ ret = 0;
+ } else {
+ if (!starting) {
+ /* start dpid */
+ if (Dpi_start_dpid() == 0) {
+ starting = 1;
+ ret = 1;
+ }
+ } else if (++starting < num_tries) {
+ /* starting */
+ ret = 1;
+ } else {
+ /* we waited too much, report an error... */
+ starting = 0;
+ }
+ }
+
+ _MSG("Dpi_check_dpid:: %s\n",
+ (ret == 0) ? "OK" : (ret == 1 ? "EAGAIN" : "ERROR"));
+ return ret;
+}
+
+
+static int Dpi_blocking_start_dpid(void)
+{
+ int cst, try = 0,
+ n_tries = 12; /* 3 seconds */
+
+ /* test the dpid, and wait a bit for it to start if necessary */
+ while ((cst = Dpi_check_dpid(n_tries)) == 1) {
+ MSG("Dpi_blocking_start_dpid: try %d\n", ++try);
+ usleep(250000); /* 1/4 sec */
+ }
+ return cst;
+}
+
+
+/*
+ * Return the dpi server's port number, or -1 on error.
+ * (A query is sent to dpid and then its answer parsed)
+ * note: as the available servers and/or the dpi socket directory can
+ * change at any time, we'll ask each time. If someday we find
+ * that connecting each time significantly degrades performance,
+ * an optimized approach can be tried.
+ */
+static int Dpi_get_server_port(const char *server_name)
+{
+ int sock_fd = -1, dpi_port = -1;
+ int dpid_port, ok = 0;
+ struct sockaddr_in sin;
+ char *cmd, *request, *rply = NULL, *port_str;
+ socklen_t sin_sz;
+
+ dReturn_val_if_fail (server_name != NULL, dpi_port);
+ _MSG("Dpi_get_server_port:: server_name = [%s]\n", server_name);
+
+ /* Read dpid's port from saved file */
+ if (Dpi_read_comm_keys(&dpid_port) != -1) {
+ ok = 1;
+ }
+ if (ok) {
+ /* Connect a socket with dpid */
+ ok = 0;
+ sin_sz = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpid_port);
+ if ((sock_fd = Dpi_make_socket_fd()) == -1 ||
+ connect(sock_fd, (struct sockaddr *)&sin, sin_sz) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ }
+ if (ok) {
+ /* ask dpid to check the dpi and send its port number back */
+ ok = 0;
+ request = a_Dpip_build_cmd("cmd=%s msg=%s", "check_server", server_name);
+ _MSG("[%s]\n", request);
+
+ if (Dpi_blocking_write(sock_fd, request, strlen(request)) == -1) {
+ MSG("Dpi_get_server_port: %s\n", dStrerror(errno));
+ } else {
+ ok = 1;
+ }
+ dFree(request);
+ }
+ if (ok) {
+ /* Get the reply */
+ ok = 0;
+ if ((rply = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG("Dpi_get_server_port: can't read server port from dpid.\n");
+ } else {
+ ok = 1;
+ }
+ }
+ if (ok) {
+ /* Parse reply */
+ ok = 0;
+ cmd = a_Dpip_get_attr(rply, "cmd");
+ if (strcmp(cmd, "send_data") == 0) {
+ port_str = a_Dpip_get_attr(rply, "msg");
+ _MSG("Dpi_get_server_port: rply=%s\n", rply);
+ _MSG("Dpi_get_server_port: port_str=%s\n", port_str);
+ dpi_port = strtol(port_str, NULL, 10);
+ dFree(port_str);
+ ok = 1;
+ }
+ dFree(cmd);
+ }
+ dFree(rply);
+ Dpi_close_fd(sock_fd);
+
+ return ok ? dpi_port : -1;
+}
+
+
+static int Dpi_connect_socket(const char *server_name, int retry)
+{
+ struct sockaddr_in sin;
+ int sock_fd, err, dpi_port, ret=-1;
+ char *cmd = NULL;
+
+ /* Query dpid for the port number for this server */
+ if ((dpi_port = Dpi_get_server_port(server_name)) == -1) {
+ _MSG("Dpi_connect_socket:: can't get port number for %s\n", server_name);
+ return -1;
+ }
+ _MSG("Dpi_connect_socket: server=%s port=%d\n", server_name, dpi_port);
+
+ /* connect with this server's socket */
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ sin.sin_port = htons(dpi_port);
+
+ if ((sock_fd = Dpi_make_socket_fd()) == -1) {
+ perror("[dpi::socket]");
+ } else if (connect(sock_fd, (void*)&sin, sizeof(sin)) == -1) {
+ err = errno;
+ sock_fd = -1;
+ MSG("[dpi::connect] errno:%d %s\n", errno, dStrerror(errno));
+ if (retry) {
+ switch (err) {
+ case ECONNREFUSED: case EBADF: case ENOTSOCK: case EADDRNOTAVAIL:
+ sock_fd = Dpi_connect_socket(server_name, FALSE);
+ break;
+ }
+ }
+
+ /* send authentication Key (the server closes sock_fd on error) */
+ } else if (!(cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey))) {
+ MSG_ERR("[Dpi_connect_socket] Can't make auth message.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[Dpi_connect_socket] Can't send auth message.\n");
+ } else {
+ ret = sock_fd;
+ }
+ dFree(cmd);
+
+ return ret;
+}
+
+
+char *a_Dpi_send_blocking_cmd(const char *server_name, const char *cmd)
+{
+ int cst, sock_fd;
+ char *ret = NULL;
+
+ /* test the dpid, and wait a bit for it to start if necessary */
+ if ((cst = Dpi_blocking_start_dpid()) != 0) {
+ return ret;
+ }
+
+ if ((sock_fd = Dpi_connect_socket(server_name, TRUE)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't connect to server.\n");
+ } else if (Dpi_blocking_write(sock_fd, cmd, strlen(cmd)) == -1) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't send message.\n");
+ } if ((ret = Dpi_blocking_read(sock_fd)) == NULL) {
+ MSG_ERR("[a_Dpi_send_blocking_cmd] Can't read message.\n");
+ }
+ Dpi_close_fd(sock_fd);
+
+ return ret;
+}
+
+
+
+void a_Cookies_set(const char *cookie, const char *host, const char *path,
+ const char *date)
+{
+ char *cmd, *dpip_tag;
+
+ if (date)
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s date=%s",
+ "set_cookie", cookie,
+ host, path, date);
+ else
+ cmd = a_Dpip_build_cmd("cmd=%s cookie=%s host=%s path=%s",
+ "set_cookie", cookie,
+ host, path);
+
+ dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
+ _MSG("a_Cookies_set: dpip_tag = {%s}\n", dpip_tag);
+ dFree(dpip_tag);
+ dFree(cmd);
+}
+
+
+char *a_Cookies_get_query(const char *scheme, const char *host,
+ const char *path)
+{
+ char *cmd, *dpip_tag, *query;
+
+ cmd = a_Dpip_build_cmd("cmd=%s scheme=%s host=%s path=%s",
+ "get_cookie", scheme,
+ host, path);
+
+ /* Get the answer from cookies.dpi */
+ _MSG("cookies.c: a_Dpi_send_blocking_cmd cmd = {%s}\n", cmd);
+ dpip_tag = a_Dpi_send_blocking_cmd("cookies", cmd);
+ _MSG("cookies.c: after a_Dpi_send_blocking_cmd resp={%s}\n", dpip_tag);
+ dFree(cmd);
+
+ if (dpip_tag != NULL) {
+ query = a_Dpip_get_attr(dpip_tag, "cookie");
+ dFree(dpip_tag);
+ } else {
+ query = dStrdup("");
+ }
+
+ return query;
+}
+
+static void expect(int lineno, const char *exp_reply,
+ const char *scheme, const char *host, const char *path)
+{
+ char *reply = a_Cookies_get_query(scheme, host, path);
+
+ if (strcmp(reply, exp_reply)) {
+ MSG("line %d: EXPECTED: %s GOT: %s\n", lineno, exp_reply, reply);
+ failed++;
+ } else {
+ passed++;
+ }
+}
+
+static void toomany()
+{
+ a_Cookies_set("1=1", "toomany.com", "/", NULL);
+ a_Cookies_set("2=1", "toomany.com", "/", NULL);
+ a_Cookies_set("3=1", "toomany.com", "/", NULL);
+ a_Cookies_set("4=1", "toomany.com", "/", NULL);
+ a_Cookies_set("5=1", "toomany.com", "/", NULL);
+ a_Cookies_set("6=1", "toomany.com", "/", NULL);
+ a_Cookies_set("7=1", "toomany.com", "/path/", NULL);
+ a_Cookies_set("8=1", "toomany.com", "/", NULL);
+ a_Cookies_set("9=1", "toomany.com", "/", NULL);
+ a_Cookies_set("10=1", "toomany.com", "/", NULL);
+ a_Cookies_set("11=1", "toomany.com", "/", NULL);
+ a_Cookies_set("12=1", "toomany.com", "/", NULL);
+ a_Cookies_set("13=1", "toomany.com", "/", NULL);
+ a_Cookies_set("14=1", "toomany.com", "/", NULL);
+ a_Cookies_set("15=1", "toomany.com", "/", NULL);
+ a_Cookies_set("16=1", "toomany.com", "/", NULL);
+ a_Cookies_set("17=1", "toomany.com", "/", NULL);
+ a_Cookies_set("18=1", "toomany.com", "/", NULL);
+ a_Cookies_set("19=1", "toomany.com", "/", NULL);
+ a_Cookies_set("20=1", "toomany.com", "/", NULL);
+ a_Cookies_set("21=1", "toomany.com", "/", NULL);
+ /* 1 was oldest and discarded */
+ expect(__LINE__, "Cookie: 7=1; 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
+ "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
+ "20=1; 21=1\r\n", "http", "toomany.com", "/path/");
+ sleep(1);
+ /* touch all of them except #7 (path matching) */
+ expect(__LINE__, "Cookie: 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
+ "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
+ "20=1; 21=1\r\n", "http", "toomany.com", "/");
+ a_Cookies_set("22=1", "toomany.com", "/", NULL);
+ /* 7 was oldest and discarded */
+ expect(__LINE__, "Cookie: 2=1; 3=1; 4=1; 5=1; 6=1; 8=1; 9=1; 10=1; "
+ "11=1; 12=1; 13=1; 14=1; 15=1; 16=1; 17=1; 18=1; 19=1; "
+ "20=1; 21=1; 22=1\r\n", "http", "toomany.com", "/path/");
+}
+
+static void maxage()
+{
+ time_t t = time(NULL)+1000;
+ char *server_date = dStrdup(ctime(&t));
+
+ a_Cookies_set("name=val; max-age=0", "maxage0.com", "/", NULL);
+ expect(__LINE__, "", "http", "maxage0.com", "/");
+
+ a_Cookies_set("name=val; max-age=100", "maxage100.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage100.com", "/");
+
+ a_Cookies_set("name=val; max-age=-100", "maxage-100.com", "/", NULL);
+ expect(__LINE__, "", "http", "maxage-100.com", "/");
+
+ a_Cookies_set("name=val; max-age=2000000000", "maxage-huge.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage-huge.com", "/");
+ /* just having a server date shouldn't matter */
+
+ a_Cookies_set("name=val; max-age=0", "maxage0s.com", "/", server_date);
+ expect(__LINE__, "", "http", "maxage0s.com", "/");
+
+ a_Cookies_set("name=val; max-age=100", "maxage100s.com", "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "maxage100s.com", "/");
+
+ a_Cookies_set("name=val; max-age=-100", "maxage-100s.com", "/",server_date);
+ expect(__LINE__, "", "http", "maxage-100s.com", "/");
+
+ /* MAX-AGE and EXPIRES */
+ a_Cookies_set("name=val; max-age=90; expires=Wed Jan 20 01:26:32 2010",
+ "maxagelater.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "maxagelater.com", "/");
+
+ a_Cookies_set("name=val; max-age=90; expires=Wed Jan 20 01:26:32 2010",
+ "maxagelaters.com", "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "maxagelaters.com", "/");
+
+ dFree(server_date);
+}
+
+static void expires_server_ahead()
+{
+ char *string;
+ time_t t = time(NULL)+1000;
+ char *server_date = dStrdup(ctime(&t));
+ time_t expt = t + 1000;
+ char *exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e2000s1000.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e2000s1000.com", "/");
+
+ a_Cookies_set(string, "e2000s1000s.com", "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e2000s1000s.com", "/");
+
+ expt = t - 500; /* past for the server, future for us */
+ dFree(exp_date);
+ exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e500s1000.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e500s1000.com", "/");
+
+ a_Cookies_set(string, "e500s1000s.com", "/", server_date);
+ expect(__LINE__, "", "http", "e500s1000s.com", "/");
+
+ expt = t; /* expire at future-for-us server date */
+ dFree(exp_date);
+ exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e1000s1000.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e1000s1000.com", "/");
+
+ a_Cookies_set(string, "e1000s1000s.com", "/", server_date);
+ expect(__LINE__, "", "http", "e1000s1000s.com", "/");
+
+ expt = time(NULL); /* now */
+ dFree(exp_date);
+ exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e0s1000.com", "/", NULL);
+ expect(__LINE__, "", "http", "e0s1000.com", "/");
+
+ a_Cookies_set(string, "e0s1000s.com", "/", server_date);
+ expect(__LINE__, "", "http", "e0s1000s.com", "/");
+
+ dFree(exp_date);
+ dFree(server_date);
+}
+
+static void expires_server_behind()
+{
+ char *string;
+ time_t t = time(NULL)-1000;
+ char *server_date = dStrdup(ctime(&t));
+
+ time_t expt = t + 1000;
+ char *exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e0s-1000.com", "/", NULL);
+ expect(__LINE__, "", "http", "e0s-1000.com", "/");
+
+ a_Cookies_set(string, "e0s-1000s.com", "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e0s-1000s.com","/");
+
+ expt = t + 500; /* future for the server, past for us */
+ dFree(exp_date);
+ exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e-500s-1000.com", "/", NULL);
+ expect(__LINE__, "", "http", "e-500s-1000.com", "/");
+
+ a_Cookies_set(string, "e-500s-1000s.com", "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "e-500s-1000s.com", "/");
+
+ expt = t; /* expire at past-for-us server date */
+ dFree(exp_date);
+ exp_date = dStrdup(ctime(&expt));
+
+ string = dStrconcat("name=val; expires=", exp_date, NULL);
+ a_Cookies_set(string, "e-1000s-1000.com", "/", NULL);
+ expect(__LINE__, "", "http", "e-1000s-1000.com", "/");
+
+ a_Cookies_set(string, "e-1000s-1000s.com", "/", server_date);
+ expect(__LINE__, "", "http", "e-1000s-1000s.com", "/");
+
+ dFree(server_date);
+ dFree(exp_date);
+}
+
+static void expires_extremes()
+{
+ time_t t;
+ char *server_date;
+
+ a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expmin.com",
+ "/", NULL);
+ expect(__LINE__, "", "http", "expmin.com", "/");
+
+ a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expneg.com",
+ "/", NULL);
+ expect(__LINE__, "", "http", "expneg.com", "/");
+
+ a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepoch.com",
+ "/", NULL);
+ expect(__LINE__, "", "http", "expepoch.com", "/");
+
+ /* TODO: revisit these tests in a few decades */
+ a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmax.com",
+ "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "expmax.com", "/");
+
+ a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmax.com",
+ "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmax.com", "/");
+
+ t = time(NULL)+1000;
+ server_date = dStrdup(ctime(&t));
+
+ a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expmina.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expmina.com", "/");
+
+ a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expnega.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expnega.com", "/");
+
+ a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepocha.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expepocha.com", "/");
+
+ a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmaxa.com",
+ "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "expmaxa.com", "/");
+
+ a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmaxa.com",
+ "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmaxa.com", "/");
+
+ t = time(NULL)-1000;
+ dFree(server_date);
+ server_date = dStrdup(ctime(&t));
+
+ a_Cookies_set("name=val; expires=Fri Dec 13 20:45:52 1901", "expminb.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expminb.com", "/");
+
+ a_Cookies_set("name=val; expires=Wed Dec 31 23:59:59 1969", "expnegb.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expnegb.com", "/");
+
+ a_Cookies_set("name=val; expires=Thu Jan 1 00:00:00 1970", "expepochb.com",
+ "/", server_date);
+ expect(__LINE__, "", "http", "expepochb.com", "/");
+
+ a_Cookies_set("name=val; expires=Tue Jan 19 03:14:07 2038", "expmaxb.com",
+ "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "expmaxb.com", "/");
+
+ a_Cookies_set("name=val; expires=Sun Jan 1 00:00:00 2040", "pastmaxb.com",
+ "/", server_date);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "pastmaxb.com", "/");
+
+ dFree(server_date);
+}
+
+static void path()
+{
+ a_Cookies_set("name=val; path=/", "p1.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p1.com", "/");
+
+ a_Cookies_set("name=val; path=/dir1", "p2.com", "/dir2", NULL);
+ expect(__LINE__, "", "http", "p2.com", "/");
+ expect(__LINE__, "", "http", "p2.com", "/d");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p2.com", "/dir1");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p2.com", "/dir1/");
+ expect(__LINE__, "", "http", "p2.com", "/dir2");
+ expect(__LINE__, "", "http", "p2.com", "/dir11");
+
+ a_Cookies_set("name=val; path=dir1", "p3.com", "/dir2", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/dir1");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p3.com", "/dir2");
+
+ a_Cookies_set("name=val; path=/dir1/", "p4.com", "/dir2", NULL);
+ expect(__LINE__, "", "http", "p4.com", "/");
+ /* this next one strikes me as a bit odd, personally, but I suppose it's not
+ * a big deal */
+ expect(__LINE__, "", "http", "p4.com", "/dir1");
+ expect(__LINE__, "", "http", "p4.com", "/dir11");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p4.com", "/dir1/");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p4.com", "/dir1/sub");
+
+ a_Cookies_set("name=val", "p5.com", "/dir/subdir", NULL);
+ expect(__LINE__, "", "http", "p5.com", "/");
+ expect(__LINE__, "", "http", "p5.com", "/bir");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p5.com", "/dir");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p5.com", "/dir/");
+
+ a_Cookies_set("name=val", "p6.com", "/dir/subdir/", NULL);
+ expect(__LINE__, "", "http", "p6.com", "/dir/");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p6.com", "/dir/subdir");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "p6.com", "/dir/subdir/s");
+}
+
+int main()
+{
+ a_Cookies_set("name=val", "ordinary.com", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "ordinary.com", "/");
+
+ toomany();
+ maxage();
+ expires_server_ahead();
+ expires_server_behind();
+ expires_extremes();
+
+ a_Cookies_set("name=val; expires=\"Sun Jan 10 00:00:00 2038\"",
+ "quoted-date.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "quoted-date.org", "/");
+
+ a_Cookies_set("name=val; expires=\"Sun Jan 11 00:00:00 1970\"",
+ "quoted-pastdate.org", "/", NULL);
+ expect(__LINE__, "", "http", "quoted-pastdate.org", "/");
+
+ path();
+
+ /* LEADING/TRAILING DOTS AND A LITTLE PUBLIC SUFFIX */
+ a_Cookies_set("name=val; domain=co.uk", "www.co.uk", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.uk", "/");
+
+ a_Cookies_set("name=val; domain=.co.uk", "www.co.uk", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.uk", "/");
+
+ a_Cookies_set("name=val; domain=co.uk.", "www.co.uk.", "/", NULL);
+ expect(__LINE__, "", "http", "www.co.uk.", "/");
+
+ a_Cookies_set("name=val; domain=.co.uk.", "www.co.uk.", "/", NULL);
+ expect(__LINE__, "", "http", ".www.co.uk.", "/");
+
+ a_Cookies_set("name=val; domain=co.org", "www.co.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "www.co.org", "/");
+
+ a_Cookies_set("name=val; domain=.cp.org", "www.cp.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "www.cp.org", "/");
+
+
+ /* DOTDOMAIN */
+ a_Cookies_set("name=val; domain=.dotdomain.org", "dotdomain.org", "/",
+ NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "dotdomain.org", "/");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "www.dotdomain.org", "/");
+
+ /* HOST_ONLY */
+ a_Cookies_set("name=val; domain=.hostonly.org", "hostonly.org", "/", NULL);
+ a_Cookies_set("name2=val2", "hostonly.org", "/", NULL);
+ a_Cookies_set("name3=val3; domain=hostonly.org", "hostonly.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=val; name2=val2; name3=val3\r\n", "http",
+ "hostonly.org", "/");
+ a_Cookies_set("name=new; domain=.hostonly.org", "hostonly.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=new; name2=val2; name3=val3\r\n", "http",
+ "hostonly.org", "/");
+ a_Cookies_set("name2=new2", "hostonly.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=new; name2=new2; name3=val3\r\n", "http",
+ "hostonly.org", "/");
+ a_Cookies_set("name3=new3; domain=hostonly.org", "hostonly.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=new; name2=new2; name3=new3\r\n", "http",
+ "hostonly.org", "/");
+
+ /* SUBDOMAIN */
+ a_Cookies_set("name=val; domain=www.subdomain.com", "subdomain.com", "/",
+ NULL);
+ a_Cookies_set("name=val; domain=.www.subdomain.com", "subdomain.com", "/",
+ NULL);
+ expect(__LINE__, "", "http", "subdomain.com", "/");
+ expect(__LINE__, "", "http", "www.subdomain.com", "/");
+
+ /* SUPERDOMAIN(?) */
+ a_Cookies_set("name=val; domain=.supdomain.com", "www.supdomain.com", "/",
+ NULL);
+ a_Cookies_set("name2=val2; domain=supdomain.com", "www.supdomain.com", "/",
+ NULL);
+ expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+ "sub2.sub.supdomain.com", "/");
+ expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+ "www.supdomain.com", "/");
+ expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+ "supdomain.com", "/");
+
+ /* UNRELATED */
+ a_Cookies_set("name=val; domain=another.com", "unrelated.com", "/", NULL);
+ expect(__LINE__, "", "http", "another.com", "/");
+ a_Cookies_set("name=val; domain=another.com", "a.org", "/", NULL);
+ expect(__LINE__, "", "http", "another.com", "/");
+ a_Cookies_set("name=val; domain=another.com", "badguys.com", "/", NULL);
+ expect(__LINE__, "", "http", "another.com", "/");
+ a_Cookies_set("name=val; domain=another.com", "more.badguys.com", "/",
+ NULL);
+ expect(__LINE__, "", "http", "another.com", "/");
+ a_Cookies_set("name=val; domain=another.com", "verybadguys.com", "/", NULL);
+ expect(__LINE__, "", "http", "another.com", "/");
+
+ a_Cookies_set("name=val; domain=similar.com", "imilar.com", "/", NULL);
+ a_Cookies_set("name2=val2; domain=similar.com", "ssimilar.com", "/", NULL);
+ a_Cookies_set("name3=val3; domain=.similar.com", "imilar.com", "/", NULL);
+ a_Cookies_set("name4=val4; domain=.similar.com", "timilar.com", "/", NULL);
+ a_Cookies_set("name4=val4; domain=.similar.com", "tiimilar.com", "/", NULL);
+ expect(__LINE__, "", "http", "similar.com", "/");
+
+ /* SECURE */
+ a_Cookies_set("name=val; secure", "secure.com", "/", NULL);
+ expect(__LINE__, "", "http", "secure.com", "/");
+ expect(__LINE__, "Cookie: name=val\r\n", "https", "secure.com", "/");
+
+ /* HTTPONLY */
+ a_Cookies_set("name=val; HttpOnly", "httponly.net", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "httponly.net", "/");
+
+ /* GIBBERISH ATTR IGNORED */
+ a_Cookies_set("name=val; ldkfals", "gibberish.net", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "gibberish.net", "/");
+
+ /* WHITESPACE/DELIMITERS */
+ a_Cookies_set(" name=val ", "whitespace.net", "/", NULL);
+ a_Cookies_set("name2=val2;", "whitespace.net", "/", NULL);
+ expect(__LINE__, "Cookie: name=val; name2=val2\r\n", "http",
+ "whitespace.net", "/");
+
+ /* NAMELESS/VALUELESS */
+ a_Cookies_set("value", "nonameval.org", "/", NULL);
+ a_Cookies_set("name=", "nonameval.org", "/", NULL);
+ a_Cookies_set("name2= ", "nonameval.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=; name2=\r\n", "http", "nonameval.org", "/");
+ a_Cookies_set("=val2", "nonameval.org", "/", NULL);
+ expect(__LINE__, "Cookie: name=; name2=\r\n", "http", "nonameval.org", "/");
+
+
+ /* SOME IP ADDRS */
+
+ a_Cookies_set("name=val", "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]",
+ "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http",
+ "[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]", "/");
+
+ a_Cookies_set("name=val", "[::FFFF:129.144.52.38]", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "[::FFFF:129.144.52.38]",
+ "/");
+
+ a_Cookies_set("name=val", "127.0.0.1", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "127.0.0.1", "/");
+
+ a_Cookies_set("name=val; domain=128.0.0.1", "128.0.0.1", "/", NULL);
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "128.0.0.1", "/");
+
+ a_Cookies_set("name=val; domain=130.0.0.1", "129.0.0.1", "/", NULL);
+ expect(__LINE__, "", "http", "129.0.0.1", "/");
+ expect(__LINE__, "", "http", "130.0.0.1", "/");
+
+ a_Cookies_set("name=val", "2.0.0.1", "/", NULL);
+ a_Cookies_set("name=bad; domain=22.0.0.1", "2.0.0.1", "/", NULL);
+ a_Cookies_set("name=bad; domain=.0.0.1", "2.0.0.1", "/", NULL);
+ a_Cookies_set("name=bad; domain=not-ip.org", "2.0.0.1", "/", NULL);
+ expect(__LINE__, "", "http", "22.0.0.1", "/");
+ expect(__LINE__, "", "http", "not-ip.org", "/");
+ expect(__LINE__, "Cookie: name=val\r\n", "http", "2.0.0.1", "/");
+
+#if 0
+HAD BEEN PLAYING AROUND WITH REAL PUBLIC SUFFIX
+a_Cookies_set("name=val;domain=sub.sub.yokohama.jp", "sub.sub.yokohama.jp", "/", NULL);
+MSG("sub sub yokohama should work: %s\n",
+ a_Cookies_get_query("http", "sub.sub.yokohama.jp", "/"));
+a_Cookies_set("name=val; domain=sub.tokyo.jp", "sub.sub.tokyo.jp", "/", NULL);
+MSG("sub tokyo jp should fail: %s\n",
+ a_Cookies_get_query("http", "sub.sub.tokyo.jp", "/"));
+a_Cookies_set("name=val; domain=pref.chiba.jp", "sub.pref.chiba.jp", "/", NULL);
+MSG("pref chiba jp should succeed: %s\n",
+ a_Cookies_get_query("http", "sub.pref.chiba.jp", "/"));
+a_Cookies_set("name=val; domain=org", "www.dillo.org", "/", NULL);
+a_Cookies_set("name=val; domain=org", "dillo.org", "/", NULL);
+a_Cookies_set("name=val; domain=org", ".dillo.org", "/", NULL);
+a_Cookies_set("name=val; domain=org.", ".dillo.org", "/", NULL);
+a_Cookies_set("name=val; domain=org.", ".dillo.org.", "/", NULL);
+MSG("org should fail: %s\n",
+ a_Cookies_get_query("http", "www.dillo.org", "/"));
+#endif
+
+ MSG("TESTS: passed: %u failed: %u\n", passed, failed);
+ return 0;
+}
diff --git a/test/dw_anchors_test.cc b/test/dw_anchors_test.cc
index 8615439e..977d724a 100644
--- a/test/dw_anchors_test.cc
+++ b/test/dw_anchors_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -57,7 +56,7 @@ static void textTimeout (void *data)
{
Textblock *oldTop = topTextblock;
topTextblock = new Textblock (false);
-
+
if (oldTop) {
oldTop->addLinebreak (wordStyle);
oldTop->addWidget (topTextblock, widgetStyle);
@@ -73,7 +72,7 @@ static void textTimeout (void *data)
buf[0] = toupper (buf[0]);
topTextblock->addText (buf, headingStyle);
topTextblock->addParbreak (5, headingStyle);
-
+
for (int i = 0; i < 30; i++) {
strcpy (buf, numbers[textblockNo]);
if (i == 0)
@@ -85,7 +84,7 @@ static void textTimeout (void *data)
}
topTextblock->flush ();
-
+
textblockNo++;
if (textblockNo < 10)
::fltk::repeat_timeout (1, textTimeout, NULL);
@@ -99,7 +98,7 @@ int main(int argc, char **argv)
platform = new FltkPlatform ();
layout = new Layout (platform);
- window = new ::fltk::Window(250, 200, "Dw Find Test");
+ window = new ::fltk::Window(250, 200, "Dw Anchors Test");
window->begin();
viewport = new FltkViewport (50, 0, 200, 200);
@@ -121,13 +120,14 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
StyleAttrs styleAttrs;
styleAttrs.initValues ();
styleAttrs.font = Font::create (layout, &fontAttrs);
styleAttrs.margin.setVal (5);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
topWidgetStyle = Style::create (layout, &styleAttrs);
styleAttrs.margin.left = 20;
diff --git a/test/dw_border_test.cc b/test/dw_border_test.cc
index 0c2b7783..b7586608 100644
--- a/test/dw_border_test.cc
+++ b/test/dw_border_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -49,7 +48,7 @@ int main(int argc, char **argv)
styleAttrs.initValues ();
styleAttrs.margin.setVal (5);
styleAttrs.borderWidth.setVal (2);
- styleAttrs.setBorderColor (Color::createShaded (layout, 0xffffff));
+ styleAttrs.setBorderColor (Color::create (layout, 0xffffff));
styleAttrs.setBorderStyle (BORDER_INSET);
styleAttrs.padding.setVal (5);
@@ -58,17 +57,18 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle1 = Style::create (layout, &styleAttrs);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffff80);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffff80);
styleAttrs.margin.setVal (0);
styleAttrs.borderWidth.setVal (1);
- styleAttrs.setBorderColor (Color::createSimple (layout, 0x4040ff));
+ styleAttrs.setBorderColor (Color::create (layout, 0x4040ff));
styleAttrs.setBorderStyle (BORDER_SOLID);
styleAttrs.padding.setVal (1);
@@ -89,7 +89,7 @@ int main(int argc, char **argv)
const char *words1[] = { "Some", "random", "text.", NULL };
const char *words2[] = { "A", "nested", "paragraph.", NULL };
-
+
for(int i = 0; words1[i]; i++) {
if(i != 0)
textblock1->addSpace (wordStyle);
@@ -98,7 +98,7 @@ int main(int argc, char **argv)
for(int i = 0; i < 1; i++) {
textblock1->addParbreak(0, wordStyle);
-
+
Textblock *textblock2 = new Textblock (false);
textblock1->addWidget (textblock2, widgetStyle2);
diff --git a/test/dw_example.cc b/test/dw_example.cc
index 75b891e2..1ce643f8 100644
--- a/test/dw_example.cc
+++ b/test/dw_example.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -50,14 +49,15 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
styleAttrs.color =
- dw::core::style::Color::createSimple (layout, 0x000000);
+ dw::core::style::Color::create (layout, 0x000000);
styleAttrs.backgroundColor =
- dw::core::style::Color::createSimple (layout, 0xffffff);
+ dw::core::style::Color::create (layout, 0xffffff);
- dw::core::style::Style *widgetStyle =
+ dw::core::style::Style *widgetStyle =
dw::core::style::Style::create (layout, &styleAttrs);
dw::Textblock *textblock = new dw::Textblock (false);
diff --git a/test/dw_find_test.cc b/test/dw_find_test.cc
index a43b4e59..a0743754 100644
--- a/test/dw_find_test.cc
+++ b/test/dw_find_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -44,11 +43,11 @@ static ::fltk::Widget *resultLabel;
static void findCallback (::fltk::Widget *widget, void *data)
{
//switch(layout->search ("worm", true)) {
- switch(layout->search ("WORM", false)) {
+ switch(layout->search ("WORM", false, false)) {
case FindtextState::SUCCESS:
resultLabel->label("SUCCESS");
break;
-
+
case FindtextState::RESTART:
resultLabel->label("RESTART");
break;
@@ -94,13 +93,14 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
StyleAttrs styleAttrs;
styleAttrs.initValues ();
styleAttrs.font = Font::create (layout, &fontAttrs);
styleAttrs.margin.setVal (10);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *topWidgetStyle = Style::create (layout, &styleAttrs);
styleAttrs.margin.setVal (0);
diff --git a/test/dw_images_scaled.cc b/test/dw_images_scaled.cc
index dfaf57b9..c9f6e76e 100644
--- a/test/dw_images_scaled.cc
+++ b/test/dw_images_scaled.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -58,13 +57,13 @@ static void imageDrawTimeout (void *data)
buf[4 * x + 2] = imgRow * 255 / 199;
buf[4 * x + 3] = (199 - imgRow) * 255 / 199;
}
-
+
imgbuf->copyRow (imgRow, buf);
- image->drawRow (imgRow);
+ image->drawRow (imgRow);
imgRow++;
}
}
-
+
if(imgRow < 200)
::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
}
@@ -80,13 +79,13 @@ static void imageDrawTimeout (void *data)
buf[3 * x + 1] = (399 - x) * 255 / 399;
buf[3 * x + 2] = imgRow * 255 / 199;
}
-
+
imgbuf->copyRow (imgRow, buf);
- image->drawRow (imgRow);
+ image->drawRow (imgRow);
imgRow++;
}
}
-
+
if(imgRow < 200)
::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
}
@@ -113,10 +112,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -133,7 +133,7 @@ int main(int argc, char **argv)
image = new dw::Image ("");
textblock->addWidget (image, imageStyle);
- textblock->addSpace (imageStyle);
+ textblock->addSpace (imageStyle);
imageStyle->unref();
diff --git a/test/dw_images_scaled2.cc b/test/dw_images_scaled2.cc
index 39c55046..175ef414 100644
--- a/test/dw_images_scaled2.cc
+++ b/test/dw_images_scaled2.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -56,14 +55,14 @@ static void imageDrawTimeout (void *data)
buf[3 * x + 1] = (399 - x) * 255 / 399;
buf[3 * x + 2] = imgRow * 255 / 199;
}
-
+
imgbuf->copyRow (imgRow, buf);
- image1->drawRow (imgRow);
- image2->drawRow (imgRow);
+ image1->drawRow (imgRow);
+ image2->drawRow (imgRow);
imgRow++;
}
}
-
+
if(imgRow < 200)
::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
}
@@ -88,10 +87,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -109,7 +109,7 @@ int main(int argc, char **argv)
Style *wordStyle = Style::create (layout, &styleAttrs);
styleAttrs.borderWidth.setVal (1);
- styleAttrs.setBorderColor (Color::createShaded (layout, 0x000080));
+ styleAttrs.setBorderColor (Color::create (layout, 0x000080));
styleAttrs.setBorderStyle (BORDER_SOLID);
styleAttrs.padding.setVal (1);
styleAttrs.backgroundColor = NULL;
diff --git a/test/dw_images_simple.cc b/test/dw_images_simple.cc
index 78629d55..39b7ed90 100644
--- a/test/dw_images_simple.cc
+++ b/test/dw_images_simple.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -41,9 +40,10 @@ static int imgRow = 0;
static void imageInitTimeout (void *data)
{
+ const bool resize = true;
//imgbuf = layout->createImgbuf (Imgbuf::RGBA, 400, 200);
imgbuf = layout->createImgbuf (Imgbuf::RGB, 400, 200);
- image->setBuffer (imgbuf);
+ image->setBuffer (imgbuf, resize);
}
/*
@@ -58,13 +58,13 @@ static void imageDrawTimeout (void *data)
buf[4 * x + 2] = imgRow * 255 / 199;
buf[4 * x + 3] = (199 - imgRow) * 255 / 199;
}
-
+
imgbuf->copyRow (imgRow, buf);
- image->drawRow (imgRow);
+ image->drawRow (imgRow);
imgRow++;
}
}
-
+
if(imgRow < 200)
::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
}
@@ -80,13 +80,13 @@ static void imageDrawTimeout (void *data)
buf[3 * x + 1] = (399 - x) * 255 / 399;
buf[3 * x + 2] = imgRow * 255 / 199;
}
-
+
imgbuf->copyRow (imgRow, buf);
- image->drawRow (imgRow);
+ image->drawRow (imgRow);
imgRow++;
}
}
-
+
if(imgRow < 200)
::fltk::repeat_timeout (0.5, imageDrawTimeout, NULL);
}
@@ -111,10 +111,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -131,7 +132,7 @@ int main(int argc, char **argv)
image = new dw::Image ("");
textblock->addWidget (image, imageStyle);
- textblock->addSpace (imageStyle);
+ textblock->addSpace (imageStyle);
imageStyle->unref();
diff --git a/test/dw_imgbuf_mem_test.cc b/test/dw_imgbuf_mem_test.cc
index 897e47f8..33131134 100644
--- a/test/dw_imgbuf_mem_test.cc
+++ b/test/dw_imgbuf_mem_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -34,7 +33,7 @@ void solution1 ()
{
FltkPlatform *platform = new FltkPlatform ();
Layout *layout = new Layout (platform);
-
+
Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
rootbuf->ref (); // Extra reference by the dicache.
printf ("=== Can be deleted? %s.\n",
@@ -49,7 +48,7 @@ void solution1 ()
printf ("=== Can be deleted? %s.\n",
rootbuf->lastReference () ? "Yes" : "No");
rootbuf->unref (); // Extra reference by the dicache.
-
+
delete layout;
}
@@ -57,7 +56,7 @@ void solution2 ()
{
FltkPlatform *platform = new FltkPlatform ();
Layout *layout = new Layout (platform);
-
+
Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
rootbuf->setDeleteOnUnref (false);
printf ("=== Can be deleted? %s.\n",
@@ -72,7 +71,7 @@ void solution2 ()
printf ("=== Can be deleted? %s.\n",
!rootbuf->isReferred () ? "Yes" : "No");
delete rootbuf;
-
+
delete layout;
}
@@ -91,13 +90,13 @@ void solution3 ()
{
FltkPlatform *platform = new FltkPlatform ();
Layout *layout = new Layout (platform);
-
+
Imgbuf *rootbuf = layout->createImgbuf (Imgbuf::RGB, 100, 100);
rootbuf->connectDeletion (new RootbufDeletionReceiver ());
Imgbuf *scaledbuf = rootbuf->getScaledBuf (50, 50);
rootbuf->unref ();
scaledbuf->unref ();
-
+
delete layout;
}
diff --git a/test/dw_links.cc b/test/dw_links.cc
index 4d15d520..44628b28 100644
--- a/test/dw_links.cc
+++ b/test/dw_links.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -33,35 +32,38 @@ using namespace dw::core;
using namespace dw::core::style;
using namespace dw::fltk;
-class LinkTestReceiver: public Widget::LinkReceiver
+class LinkTestReceiver: public Layout::LinkReceiver
{
- bool enter (Widget *widget, int link, int x, int y);
- bool press (Widget *widget, int link, int x, int y, EventButton *event);
- bool release (Widget *widget, int link, int x, int y, EventButton *event);
- bool click (Widget *widget, int link, int x, int y, EventButton *event);
+ bool enter (Widget *widget, int link, int img, int x, int y);
+ bool press (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool release (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool click (Widget *widget, int link, int img,
+ int x, int y, EventButton *event);
};
-bool LinkTestReceiver::enter (Widget *widget, int link, int x, int y)
+bool LinkTestReceiver::enter (Widget *widget, int link, int img, int x, int y)
{
printf ("enter: %d\n", link);
return true;
}
-bool LinkTestReceiver::press (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::press (Widget *widget, int link, int img, int x, int y,
EventButton *event)
{
printf ("press: %d\n", link);
return true;
}
-bool LinkTestReceiver::release (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::release (Widget *widget, int link, int img, int x,int y,
EventButton *event)
{
printf ("release: %d\n", link);
return true;
}
-bool LinkTestReceiver::click (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::click (Widget *widget, int link, int img, int x, int y,
EventButton *event)
{
printf ("click: %d\n", link);
@@ -89,10 +91,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -100,7 +103,7 @@ int main(int argc, char **argv)
textblock->setStyle (widgetStyle);
layout->setWidget (textblock);
- textblock->connectLink (&linkTestReceiver);
+ layout->connectLink (&linkTestReceiver);
widgetStyle->unref();
@@ -110,10 +113,10 @@ int main(int argc, char **argv)
Style *wordStyle = Style::create (layout, &styleAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x0000ff);
+ styleAttrs.color = Color::create (layout, 0x0000ff);
styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE;
styleAttrs.cursor = CURSOR_POINTER;
-
+
for(int i = 1; i <= 10; i++) {
char buf[4];
sprintf(buf, "%d.", i);
@@ -130,15 +133,15 @@ int main(int argc, char **argv)
textblock->addText(words1[j], wordStyle);
textblock->addSpace(wordStyle);
}
-
+
styleAttrs.x_link = i;
Style *linkStyle = Style::create (layout, &styleAttrs);
-
+
for(int j = 0; words2[j]; j++) {
textblock->addText(words2[j], linkStyle);
textblock->addSpace(wordStyle);
}
-
+
linkStyle->unref ();
textblock->addParbreak(10, wordStyle);
diff --git a/test/dw_links2.cc b/test/dw_links2.cc
index e1db9011..294950af 100644
--- a/test/dw_links2.cc
+++ b/test/dw_links2.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -33,35 +32,38 @@ using namespace dw::core;
using namespace dw::core::style;
using namespace dw::fltk;
-class LinkTestReceiver: public Widget::LinkReceiver
+class LinkTestReceiver: public Layout::LinkReceiver
{
- bool enter (Widget *widget, int link, int x, int y);
- bool press (Widget *widget, int link, int x, int y, EventButton *event);
- bool release (Widget *widget, int link, int x, int y, EventButton *event);
- bool click (Widget *widget, int link, int x, int y, EventButton *event);
+ bool enter (Widget *widget, int link, int img, int x, int y);
+ bool press (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool release (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
+ bool click (Widget *widget, int link, int img, int x, int y,
+ EventButton *event);
};
-bool LinkTestReceiver::enter (Widget *widget, int link, int x, int y)
+bool LinkTestReceiver::enter (Widget *widget, int link, int img, int x, int y)
{
printf ("enter: %d\n", link);
return true;
}
-bool LinkTestReceiver::press (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::press (Widget *widget, int link, int img, int x, int y,
EventButton *event)
{
printf ("press: %d\n", link);
return true;
}
-bool LinkTestReceiver::release (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::release (Widget *widget, int link, int img, int x,int y,
EventButton *event)
{
printf ("release: %d\n", link);
return true;
}
-bool LinkTestReceiver::click (Widget *widget, int link, int x, int y,
+bool LinkTestReceiver::click (Widget *widget, int link, int img, int x, int y,
EventButton *event)
{
printf ("click: %d\n", link);
@@ -76,7 +78,7 @@ int main(int argc, char **argv)
FltkPlatform *platform = new FltkPlatform ();
Layout *layout = new Layout (platform);
- ::fltk::Window *window = new ::fltk::Window(200, 300, "Dw Links");
+ ::fltk::Window *window = new ::fltk::Window(200, 300, "Dw Links2");
window->begin();
::fltk::Widget *Panel = new ::fltk::Widget(0, 0, ww, lh, "CONTROL PANEL");
@@ -117,10 +119,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -128,7 +131,7 @@ int main(int argc, char **argv)
textblock->setStyle (widgetStyle);
layout->setWidget (textblock);
- textblock->connectLink (new LinkTestReceiver ());
+ layout->connectLink (new LinkTestReceiver ());
widgetStyle->unref();
@@ -138,10 +141,10 @@ int main(int argc, char **argv)
Style *wordStyle = Style::create (layout, &styleAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x0000ff);
+ styleAttrs.color = Color::create (layout, 0x0000ff);
styleAttrs.textDecoration = TEXT_DECORATION_UNDERLINE;
styleAttrs.cursor = CURSOR_POINTER;
-
+
for(int i = 1; i <= 30; i++) {
char buf[4];
sprintf(buf, "%d.", i);
@@ -158,15 +161,15 @@ int main(int argc, char **argv)
textblock->addText (words1[j], wordStyle);
textblock->addSpace(wordStyle);
}
-
+
styleAttrs.x_link = i;
Style *linkStyle = Style::create (layout, &styleAttrs);
-
+
for(int j = 0; words2[j]; j++) {
textblock->addText (words2[j], linkStyle);
textblock->addSpace(wordStyle);
}
-
+
linkStyle->unref ();
textblock->addParbreak(10, wordStyle);
diff --git a/test/dw_lists.cc b/test/dw_lists.cc
index 99e78149..2725c34d 100644
--- a/test/dw_lists.cc
+++ b/test/dw_lists.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -54,10 +53,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -75,8 +75,8 @@ int main(int argc, char **argv)
styleAttrs.margin.setVal (5);
styleAttrs.padding.setVal (5);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffff40);
- styleAttrs.setBorderColor (Color::createSimple (layout, 0x000000));
+ styleAttrs.backgroundColor = Color::create (layout, 0xffff40);
+ styleAttrs.setBorderColor (Color::create (layout, 0x000000));
styleAttrs.setBorderStyle (BORDER_SOLID);
styleAttrs.borderWidth.setVal (1);
@@ -90,7 +90,7 @@ int main(int argc, char **argv)
"comes", "some", "more", "text", "to",
"demonstrate", "word", "wrapping.", NULL };
-
+
for(int i = 0; wordsPar[i]; i++) {
if(i != 0)
textblock->addSpace (wordStyle);
@@ -109,7 +109,7 @@ int main(int argc, char **argv)
char buf[16];
sprintf (buf, "%d.", i);
- listItem->initWithText (strdup (buf), wordStyle);
+ listItem->initWithText (buf, wordStyle);
for(int j = 0; wordsItem[j]; j++) {
if(j != 0)
diff --git a/test/dw_resource_test.cc b/test/dw_resource_test.cc
index fc825836..28611bb0 100644
--- a/test/dw_resource_test.cc
+++ b/test/dw_resource_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -40,7 +39,7 @@ int main(int argc, char **argv)
FltkPlatform *platform = new FltkPlatform ();
Layout *layout = new Layout (platform);
- ::fltk::Window *window = new ::fltk::Window(410, 210, "Dw Simple Image");
+ ::fltk::Window *window = new ::fltk::Window(410, 210, "Dw Resource test");
window->begin();
FltkViewport *viewport = new FltkViewport (0, 0, 410, 210);
@@ -55,10 +54,11 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
Style *widgetStyle = Style::create (layout, &styleAttrs);
@@ -72,10 +72,10 @@ int main(int argc, char **argv)
styleAttrs.backgroundColor = NULL;
SelectionResource *res = layout->getResourceFactory()->createListResource
- (ListResource::SELECTION_AT_MOST_ONE);
+ (ListResource::SELECTION_AT_MOST_ONE, 4);
//SelectionResource *res =
// layout->getResourceFactory()->createOptionMenuResource ();
-
+
Embed *embed = new Embed (res);
textblock->addWidget (embed, widgetStyle);
textblock->addSpace (widgetStyle);
diff --git a/test/dw_table.cc b/test/dw_table.cc
index d269d551..b0e9c4db 100644
--- a/test/dw_table.cc
+++ b/test/dw_table.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -51,9 +50,9 @@ int main(int argc, char **argv)
styleAttrs.padding.setVal (0);
styleAttrs.borderWidth.setVal (1);
styleAttrs.setBorderStyle (BORDER_OUTSET);
- styleAttrs.setBorderColor (Color::createShaded (layout, 0xffffff));
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.setBorderColor (Color::create (layout, 0xffffff));
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
styleAttrs.hBorderSpacing = 5;
styleAttrs.vBorderSpacing = 5;
@@ -62,6 +61,7 @@ int main(int argc, char **argv)
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
Style *tableStyle = Style::create (layout, &styleAttrs);
diff --git a/test/dw_table_aligned.cc b/test/dw_table_aligned.cc
index beb8525d..67cf21f3 100644
--- a/test/dw_table_aligned.cc
+++ b/test/dw_table_aligned.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -50,17 +49,18 @@ int main(int argc, char **argv)
styleAttrs.margin.setVal (5);
styleAttrs.borderWidth.setVal (1);
styleAttrs.setBorderStyle (BORDER_OUTSET);
- styleAttrs.setBorderColor (Color::createShaded (layout, 0x808080));
+ styleAttrs.setBorderColor (Color::create (layout, 0x808080));
FontAttrs fontAttrs;
fontAttrs.name = "Bitstream Charter";
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xa0a0a0);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xa0a0a0);
styleAttrs.hBorderSpacing = 5;
styleAttrs.vBorderSpacing = 5;
@@ -74,7 +74,7 @@ int main(int argc, char **argv)
styleAttrs.borderWidth.setVal (1);
styleAttrs.setBorderStyle (BORDER_INSET);
-
+
Style *cellStyle = Style::create (layout, &styleAttrs);
styleAttrs.borderWidth.setVal (0);
@@ -104,7 +104,7 @@ int main(int argc, char **argv)
cell->addText (buf, wordStyle);
cell->flush ();
- }
+ }
wordStyle->unref();
cellStyle->unref();
diff --git a/test/dw_ui_test.cc b/test/dw_ui_test.cc
index 5ce949ef..2bc35893 100644
--- a/test/dw_ui_test.cc
+++ b/test/dw_ui_test.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -53,14 +52,15 @@ int main(int argc, char **argv)
StyleAttrs styleAttrs;
styleAttrs.initValues ();
styleAttrs.margin.setVal (5);
- styleAttrs.color = Color::createSimple (layout, 0x000000);
- styleAttrs.backgroundColor = Color::createSimple (layout, 0xffffff);
+ styleAttrs.color = Color::create (layout, 0x000000);
+ styleAttrs.backgroundColor = Color::create (layout, 0xffffff);
FontAttrs fontAttrs;
fontAttrs.name = "Helvetica";
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
styleAttrs.font = Font::create (layout, &fontAttrs);
Style *tableStyle = Style::create (layout, &styleAttrs);
@@ -79,10 +79,10 @@ int main(int argc, char **argv)
// First of all, the resources. Later, they are embedded into the
// widget tree.
EntryResource *entryres1 =
- layout->getResourceFactory()->createEntryResource (10, false);
+ layout->getResourceFactory()->createEntryResource (10, false, NULL);
entryres1->setText ("Hi!");
EntryResource *entryres2 =
- layout->getResourceFactory()->createEntryResource (10, true);
+ layout->getResourceFactory()->createEntryResource (10, true, NULL);
MultiLineTextResource *textres =
layout->getResourceFactory()->createMultiLineTextResource (15,3);
RadioButtonResource *radiores1 =
@@ -95,13 +95,13 @@ int main(int argc, char **argv)
SelectionResource *selres[2];
selres[0] = layout->getResourceFactory()->createOptionMenuResource ();
selres[1] = layout->getResourceFactory()->createListResource
- (ListResource::SELECTION_AT_MOST_ONE);
+ (ListResource::SELECTION_AT_MOST_ONE, 4);
LabelButtonResource *buttonres =
layout->getResourceFactory()->createLabelButtonResource ("Run!");
// Note on complex buttons: before any operations on the widget, which
// need a layout, the complex button resource should be created, since
- // then, a layout and a platform are instanciated.
+ // then, a layout and a platform are instantiated.
Textblock *cbuttontext = new Textblock(false);
ComplexButtonResource *cbuttonres =
layout->getResourceFactory()->createComplexButtonResource (cbuttontext,
diff --git a/test/fltk_browser.cc b/test/fltk_browser.cc
index 4186156e..11dfcbeb 100644
--- a/test/fltk_browser.cc
+++ b/test/fltk_browser.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -40,7 +39,7 @@ int main (int argc, char *argv[])
new Item ("second");
new Item ("third");
}
-
+
window->resizable(browser);
window->show();
return run();
diff --git a/test/form.cc b/test/form.cc
index 5e8e0471..4bebd520 100644
--- a/test/form.cc
+++ b/test/form.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -50,7 +49,7 @@ const char *Form::TextResourceDecorator::getValue ()
Form::RadioButtonResourceDecorator::RadioButtonResourceDecorator
(const char *name, RadioButtonResource *resource, const char **values):
- Form::ResourceDecorator (name)
+ Form::ResourceDecorator (name)
{
this->resource = resource;
@@ -60,7 +59,7 @@ Form::RadioButtonResourceDecorator::RadioButtonResourceDecorator
this->values = new const char*[n + 1];
for(int i = 0; i < n; i++)
this->values[i] = strdup (values[i]);
- values[n] = 0;
+ this->values[n] = 0;
}
Form::RadioButtonResourceDecorator::~RadioButtonResourceDecorator ()
@@ -100,7 +99,7 @@ const char *Form::CheckButtonResourceDecorator::getValue ()
Form::SelectionResourceDecorator::SelectionResourceDecorator
(const char *name, SelectionResource *resource, const char **values):
- Form::ResourceDecorator (name)
+ Form::ResourceDecorator (name)
{
this->resource = resource;
@@ -163,18 +162,18 @@ Form::FormClickedReceiver::~FormClickedReceiver ()
delete name;
delete[] value;
}
-
-void Form::FormClickedReceiver::clicked (ButtonResource *resource,
- int buttonNo, int x, int y)
+
+void Form::FormClickedReceiver::clicked (Resource *resource,
+ dw::core::EventButton *event)
{
- form->send (name, value, x, y);
+ form->send (name, value, event->xCanvas, event->yCanvas);
}
Form::Form ()
{
resources = new lout::container::typed::List <ResourceDecorator> (true);
activateReceiver = new FormActivateReceiver (this);
- clickedReceivers =
+ clickedReceivers =
new lout::container::typed::List <FormClickedReceiver> (true);
}
diff --git a/test/form.hh b/test/form.hh
index a04460f4..6235c6cc 100644
--- a/test/form.hh
+++ b/test/form.hh
@@ -55,7 +55,7 @@ private:
/**
* \brief Decorates instances of dw::core::ui::RadioButtonResource.
*
- * This class has to be instanciated only once for a group of radio
+ * This class has to be instantiated only once for a group of radio
* buttons.
*/
class RadioButtonResourceDecorator: public ResourceDecorator
@@ -113,14 +113,14 @@ private:
public:
inline FormActivateReceiver (Form *form) { this->form = form; }
-
+
void activate (dw::core::ui::Resource *resource);
void enter (dw::core::ui::Resource *resource);
void leave (dw::core::ui::Resource *resource);
};
class FormClickedReceiver:
- public dw::core::ui::ButtonResource::ClickedReceiver
+ public dw::core::ui::Resource::ClickedReceiver
{
private:
Form *form;
@@ -129,9 +129,9 @@ private:
public:
FormClickedReceiver (Form *form, const char *name, const char *value);
~FormClickedReceiver ();
-
- void clicked (dw::core::ui::ButtonResource *resource, int buttonNo,
- int x, int y);
+
+ void clicked(dw::core::ui::Resource *resource,
+ dw::core::EventButton *event);
};
lout::container::typed::List <ResourceDecorator> *resources;
diff --git a/test/shapes.cc b/test/shapes.cc
index 8d33152b..30aa7d81 100644
--- a/test/shapes.cc
+++ b/test/shapes.cc
@@ -14,8 +14,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -23,7 +22,7 @@
#include "../dw/core.hh"
using namespace dw::core;
-using namespace misc;
+using namespace lout::misc;
int main()
{